dsh-cloud 0.2.0__tar.gz → 0.2.2__tar.gz
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.
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/PKG-INFO +1 -1
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/pyproject.toml +1 -1
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/cli.py +44 -9
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/release-manifest.json +3 -3
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/config/i18n/en.json +467 -462
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/config/i18n/zh.json +467 -462
- dsh_cloud-0.2.2/src/dsh_cloud_cli/wizard.py +203 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/LICENSE +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/README.md +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/build_backend.py +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/__init__.py +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/__main__.py +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/Caddyfile +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/compose.build.yml +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/compose.postgres.yml +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/config/models.curated.txt +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/config/models.json +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/config/pricing.cny.json +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/config/pricing.eur.json +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/config/pricing.gbp.json +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/config/pricing.hkd.json +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/config/pricing.jpy.json +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/config/pricing.usd.json +0 -0
- {dsh_cloud-0.2.0 → dsh_cloud-0.2.2}/src/dsh_cloud_cli/templates/docker-compose.yml +0 -0
|
@@ -5,8 +5,12 @@ import os
|
|
|
5
5
|
import re
|
|
6
6
|
import secrets
|
|
7
7
|
import shutil
|
|
8
|
+
import re
|
|
9
|
+
import shutil
|
|
8
10
|
import subprocess
|
|
9
11
|
import sys
|
|
12
|
+
|
|
13
|
+
from .wizard import apply_answers, command_prefix, next_steps, prompt_answers, should_run_wizard
|
|
10
14
|
from pathlib import Path
|
|
11
15
|
|
|
12
16
|
|
|
@@ -173,7 +177,7 @@ def template_roots() -> tuple[Path, Path]:
|
|
|
173
177
|
return root / "deploy/selfhost", root / "server/config"
|
|
174
178
|
|
|
175
179
|
|
|
176
|
-
def generated_env(parsed: dict, manifest: dict) -> str:
|
|
180
|
+
def generated_env(parsed: dict, manifest: dict, answers: dict | None = None) -> str:
|
|
177
181
|
options = parsed["options"]
|
|
178
182
|
mode = str(options.get("mode", "trial"))
|
|
179
183
|
trial = mode == "trial"
|
|
@@ -184,12 +188,17 @@ def generated_env(parsed: dict, manifest: dict) -> str:
|
|
|
184
188
|
values = [
|
|
185
189
|
("COMPOSE_PROJECT_NAME", str(options.get("projectName", "dsh-selfhost"))),
|
|
186
190
|
("DOMAIN", domain), ("SITE_SCHEME", "http" if trial else "https"),
|
|
187
|
-
|
|
191
|
+
# 键序与 src/wizard.mjs 的那份保持一致 —— 两个安装器的 .env 逐字节相同,
|
|
192
|
+
# 由 tests/distribution 的 parity 用例钉着。
|
|
193
|
+
("PUBLIC_BASE", public_base),
|
|
188
194
|
("DSH_SITE", TRIAL_CADDY_SITE if trial else f"https://{domain}"),
|
|
195
|
+
("BIND_ADDRESS", "127.0.0.1" if trial else "0.0.0.0"),
|
|
189
196
|
("HTTP_PORT", "8787" if trial else "80"), ("HTTPS_PORT", "8443" if trial else "443"),
|
|
190
197
|
("DHC_DEV", "1" if trial else "0"), ("DHC_CONFIG_DIR", "./config"),
|
|
191
198
|
("PRICING_FILE", "pricing.cny.json"), ("AUTH_SECRET_FILE", "/run/secrets/auth_secret"),
|
|
192
199
|
("UPSTREAM_BASE_URL", "https://api.deepseek.com/v1"), ("UPSTREAM_API_KEY", ""),
|
|
200
|
+
# 联网搜索: 留空则 web_search 不可用。zhipu 走 open.bigmodel.cn。
|
|
201
|
+
("SEARCH_PROVIDER", "zhipu"), ("ZHIPU_SEARCH_API_KEY", ""),
|
|
193
202
|
("ADMIN_EMAILS", str(options.get("adminEmail", ""))),
|
|
194
203
|
("MAIL_SMTP_HOST", ""), ("MAIL_SMTP_USER", ""), ("MAIL_SMTP_PASS", ""), ("MAIL_FROM", ""),
|
|
195
204
|
("GOOGLE_LOGIN_CLIENT_ID", ""), ("GOOGLE_LOGIN_CLIENT_SECRET", ""),
|
|
@@ -197,13 +206,21 @@ def generated_env(parsed: dict, manifest: dict) -> str:
|
|
|
197
206
|
("DHC_SERVER_IMAGE", manifest["images"]["server"]),
|
|
198
207
|
("CADDY_IMAGE", manifest["baseImages"]["caddy"]),
|
|
199
208
|
("POSTGRES_IMAGE", manifest["baseImages"]["postgres"]),
|
|
209
|
+
# 云工作台。自部署给了域名就默认开 —— 那是招牌功能, 装完没有它等于交付了
|
|
210
|
+
# 半个东西。还差一条 DNS 记录 (work.<域名> 指向本机), 装完面板会说。试用
|
|
211
|
+
# 模式必定关: localhost 与 work.localhost 是不同 host, 会话 cookie 过不去。
|
|
212
|
+
("WORK_ENABLED", "0" if trial else "1"),
|
|
213
|
+
("WORK_DOMAIN", "" if trial else f"work.{domain}"),
|
|
214
|
+
("COOKIE_DOMAIN", "" if trial else f".{domain}"),
|
|
215
|
+
("COMPOSE_PROFILES", "" if trial else "work"),
|
|
200
216
|
("SOCKET_PROXY_IMAGE", manifest["baseImages"]["socketProxy"]),
|
|
201
217
|
("WORK_IMAGE", manifest["images"]["workspace"]),
|
|
202
218
|
]
|
|
219
|
+
values = apply_answers(values, answers or {})
|
|
203
220
|
return "".join(f"{key}={value}\n" for key, value in values)
|
|
204
221
|
|
|
205
222
|
|
|
206
|
-
def initialize(parsed: dict, manifest: dict, deployment_plan: dict | None = None) -> Path:
|
|
223
|
+
def initialize(parsed: dict, manifest: dict, deployment_plan: dict | None = None, answers: dict | None = None) -> Path:
|
|
207
224
|
deployment_plan = deployment_plan or plan(parsed, manifest)
|
|
208
225
|
target = target_of(parsed)
|
|
209
226
|
if target.exists():
|
|
@@ -226,7 +243,7 @@ def initialize(parsed: dict, manifest: dict, deployment_plan: dict | None = None
|
|
|
226
243
|
shutil.copytree(config, target / "config")
|
|
227
244
|
secret = os.environ.get("DSH_CLOUD_TEST_RANDOM_HEX", secrets.token_hex(32))
|
|
228
245
|
atomic_write(target / "secrets/auth_secret", f"{secret}\n", 0o600)
|
|
229
|
-
atomic_write(target / ".env", generated_env(parsed, manifest), 0o600)
|
|
246
|
+
atomic_write(target / ".env", generated_env(parsed, manifest, answers), 0o600)
|
|
230
247
|
atomic_write(target / ".gitignore", ".env\nsecrets/\n.dsh-cloud/lock\n")
|
|
231
248
|
state = {
|
|
232
249
|
"schemaVersion": 1, "version": manifest["version"], "stackSchema": manifest["stackSchema"],
|
|
@@ -307,14 +324,19 @@ def execute(parsed: dict) -> dict:
|
|
|
307
324
|
if parsed.get("special") == "version":
|
|
308
325
|
return {"text": manifest["version"] + "\n"}
|
|
309
326
|
value = plan(parsed, manifest)
|
|
327
|
+
state = Path(value["directory"]) / ".dsh-cloud/state.json"
|
|
328
|
+
fresh_init = not state.is_file()
|
|
329
|
+
# 只有全新部署才问; 已初始化的目录再问一遍等于诱导用户覆盖自己的配置。
|
|
330
|
+
answers: dict = {}
|
|
331
|
+
if should_run_wizard(parsed, is_tty=sys.stdin.isatty() and sys.stdout.isatty(), fresh_init=fresh_init):
|
|
332
|
+
answers = prompt_answers(version=manifest["version"], mode=str(parsed["options"].get("mode", "trial")))
|
|
310
333
|
if parsed["command"] == "init":
|
|
311
334
|
if parsed["options"].get("dryRun"):
|
|
312
335
|
return {"json": value}
|
|
313
|
-
initialize(parsed, manifest, value)
|
|
336
|
+
initialize(parsed, manifest, value, answers)
|
|
314
337
|
return {"json": {**value, "initialized": True}}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
initialize(parsed, manifest, value)
|
|
338
|
+
if parsed["command"] == "start" and fresh_init and not parsed["options"].get("dryRun"):
|
|
339
|
+
initialize(parsed, manifest, value, answers)
|
|
318
340
|
if state.is_file():
|
|
319
341
|
restore_deployment(value, state, parsed)
|
|
320
342
|
if parsed["options"].get("dryRun"):
|
|
@@ -347,7 +369,20 @@ def execute(parsed: dict) -> dict:
|
|
|
347
369
|
if result.stderr.strip():
|
|
348
370
|
response["composeError"] = result.stderr.strip()
|
|
349
371
|
return {"json": response}
|
|
350
|
-
|
|
372
|
+
if parsed["command"] not in {"start", "up"}:
|
|
373
|
+
return {"text": ""}
|
|
374
|
+
# 人在终端前就给完整指引; 管道/CI 里保持裸 URL, 免得打断既有脚本的解析。
|
|
375
|
+
if not sys.stdout.isatty():
|
|
376
|
+
return {"text": value["url"] + "\n"}
|
|
377
|
+
env_text = (Path(value["directory"]) / ".env").read_text(encoding="utf-8")
|
|
378
|
+
has_key = bool(re.search(r"^UPSTREAM_API_KEY=.+$", env_text, re.MULTILINE))
|
|
379
|
+
prefix = command_prefix(resolved=shutil.which("dsh-cloud") or "", entry=sys.argv[0])
|
|
380
|
+
return {"text": next_steps(url=value["url"], directory=value["directory"],
|
|
381
|
+
has_upstream_key=has_key, project_name=value["projectName"],
|
|
382
|
+
prefix=prefix,
|
|
383
|
+
work_domain=(f"work.{parsed['options'].get('domain')}"
|
|
384
|
+
if value["mode"] == "selfhost" else ""),
|
|
385
|
+
dev_mail=value["mode"] != "selfhost")}
|
|
351
386
|
|
|
352
387
|
|
|
353
388
|
def main(argv: list[str] | None = None) -> int:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"license": "LicenseRef-DSH-Cloud-Community-1.0",
|
|
5
5
|
"stackSchema": 1,
|
|
6
6
|
"databaseSchema": 1,
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"minUpgradeFrom": "0.1.0",
|
|
9
9
|
"harnessRuntime": "0.1.0-rc.8",
|
|
10
10
|
"images": {
|
|
11
|
-
"server": "ghcr.io/agentsdanceai/dsh-cloud-server:0.2.
|
|
12
|
-
"workspace": "ghcr.io/agentsdanceai/dsh-cloud-workspace:0.2.
|
|
11
|
+
"server": "ghcr.io/agentsdanceai/dsh-cloud-server:0.2.2",
|
|
12
|
+
"workspace": "ghcr.io/agentsdanceai/dsh-cloud-workspace:0.2.2"
|
|
13
13
|
},
|
|
14
14
|
"baseImages": {
|
|
15
15
|
"python": "python:3.11-slim-bookworm@sha256:2e32f7d302adc1c37428355c1e646897c0c53f4fd60b6a551245fb90ee129f91",
|