jishushell 0.4.24 → 0.5.15
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/INSTALL-NOTICE +11 -0
- package/apps/anythingllm-container.yaml +287 -0
- package/apps/browserless-chromium-container.yaml +90 -0
- package/apps/filebrowser-container.yaml +163 -0
- package/apps/hermes-container.yaml +36 -2
- package/apps/ollama-binary.yaml +91 -90
- package/apps/ollama-cpu-container.yaml +8 -1
- package/apps/ollama-with-hollama-binary.yaml +91 -90
- package/apps/openclaw-binary.yaml +38 -1
- package/apps/openclaw-container.yaml +45 -2
- package/apps/openclaw-with-ollama-container.yaml +11 -2
- package/apps/openclaw-with-searxng-container.yaml +26 -2
- package/apps/openwebui-container.yaml +45 -1
- package/apps/playwright-container.yaml +7 -1
- package/apps/searxng-container.yaml +58 -7
- package/apps/weknora-container.yaml +471 -0
- package/dist/cli/app.js +79 -9
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/doctor.d.ts +12 -12
- package/dist/cli/doctor.js +242 -55
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/llm.d.ts +4 -3
- package/dist/cli/llm.js +4 -3
- package/dist/cli/llm.js.map +1 -1
- package/dist/cli/panel.d.ts +6 -5
- package/dist/cli/panel.js +10 -9
- package/dist/cli/panel.js.map +1 -1
- package/dist/config.d.ts +19 -0
- package/dist/config.js +99 -1
- package/dist/config.js.map +1 -1
- package/dist/control.d.ts +7 -6
- package/dist/control.js +7 -6
- package/dist/control.js.map +1 -1
- package/dist/install.js +3 -3
- package/dist/install.js.map +1 -1
- package/dist/routes/agent-apps.d.ts +1 -1
- package/dist/routes/agent-apps.js +1 -1
- package/dist/routes/apps.js +44 -11
- package/dist/routes/apps.js.map +1 -1
- package/dist/routes/auth.js +5 -2
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/backup.js +64 -11
- package/dist/routes/backup.js.map +1 -1
- package/dist/routes/external-mounts.d.ts +17 -0
- package/dist/routes/external-mounts.js +73 -0
- package/dist/routes/external-mounts.js.map +1 -0
- package/dist/routes/file-mounts.d.ts +13 -0
- package/dist/routes/file-mounts.js +90 -0
- package/dist/routes/file-mounts.js.map +1 -0
- package/dist/routes/files-organize.d.ts +28 -0
- package/dist/routes/files-organize.js +167 -0
- package/dist/routes/files-organize.js.map +1 -0
- package/dist/routes/files.d.ts +31 -0
- package/dist/routes/files.js +321 -0
- package/dist/routes/files.js.map +1 -0
- package/dist/routes/instances.js +826 -17
- package/dist/routes/instances.js.map +1 -1
- package/dist/routes/internal.d.ts +2 -0
- package/dist/routes/internal.js +59 -0
- package/dist/routes/internal.js.map +1 -0
- package/dist/routes/llm.js +24 -35
- package/dist/routes/llm.js.map +1 -1
- package/dist/routes/setup.js +10 -10
- package/dist/routes/setup.js.map +1 -1
- package/dist/routes/system.js +1 -1
- package/dist/routes/system.js.map +1 -1
- package/dist/routes/webdav.d.ts +17 -0
- package/dist/routes/webdav.js +114 -0
- package/dist/routes/webdav.js.map +1 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.js +751 -20
- package/dist/server.js.map +1 -1
- package/dist/services/agent-apps/catalog.js +4 -3
- package/dist/services/agent-apps/catalog.js.map +1 -1
- package/dist/services/agent-apps/index.d.ts +1 -1
- package/dist/services/agent-apps/index.js +1 -1
- package/dist/services/agent-apps/installers/adapter.d.ts +1 -1
- package/dist/services/agent-apps/installers/adapter.js +1 -1
- package/dist/services/agent-apps/installers/shell-script.d.ts +1 -1
- package/dist/services/agent-apps/installers/shell-script.js +3 -3
- package/dist/services/agent-apps/installers/shell-script.js.map +1 -1
- package/dist/services/agent-apps/types.d.ts +2 -2
- package/dist/services/agent-apps/types.js +1 -1
- package/dist/services/app/app-compiler.d.ts +1 -1
- package/dist/services/app/app-compiler.js +5 -5
- package/dist/services/app/app-compiler.js.map +1 -1
- package/dist/services/app/app-manager.d.ts +25 -1
- package/dist/services/app/app-manager.js +829 -150
- package/dist/services/app/app-manager.js.map +1 -1
- package/dist/services/app/custom-manager.js.map +1 -1
- package/dist/services/app/hermes-agent-manager.js +7 -4
- package/dist/services/app/hermes-agent-manager.js.map +1 -1
- package/dist/services/app/ollama-manager.js +1 -1
- package/dist/services/app/ollama-manager.js.map +1 -1
- package/dist/services/app/openclaw-manager.js +20 -3
- package/dist/services/app/openclaw-manager.js.map +1 -1
- package/dist/services/app/platform-transform.d.ts +32 -0
- package/dist/services/app/platform-transform.js +65 -0
- package/dist/services/app/platform-transform.js.map +1 -0
- package/dist/services/app/provide-resolver.d.ts +29 -0
- package/dist/services/app/provide-resolver.js +112 -0
- package/dist/services/app/provide-resolver.js.map +1 -0
- package/dist/services/app-passwords.d.ts +61 -0
- package/dist/services/app-passwords.js +173 -0
- package/dist/services/app-passwords.js.map +1 -0
- package/dist/services/backup-manager.d.ts +11 -0
- package/dist/services/backup-manager.js +177 -4
- package/dist/services/backup-manager.js.map +1 -1
- package/dist/services/capability-endpoint-validator.d.ts +41 -0
- package/dist/services/capability-endpoint-validator.js +104 -0
- package/dist/services/capability-endpoint-validator.js.map +1 -0
- package/dist/services/capability-health.d.ts +16 -0
- package/dist/services/capability-health.js +121 -0
- package/dist/services/capability-health.js.map +1 -0
- package/dist/services/capability-registry.d.ts +106 -0
- package/dist/services/capability-registry.js +313 -0
- package/dist/services/capability-registry.js.map +1 -0
- package/dist/services/connection-apply.d.ts +91 -0
- package/dist/services/connection-apply.js +475 -0
- package/dist/services/connection-apply.js.map +1 -0
- package/dist/services/connection-resolver.d.ts +65 -0
- package/dist/services/connection-resolver.js +281 -0
- package/dist/services/connection-resolver.js.map +1 -0
- package/dist/services/connection-transactor.d.ts +39 -0
- package/dist/services/connection-transactor.js +351 -0
- package/dist/services/connection-transactor.js.map +1 -0
- package/dist/services/external-mounts.d.ts +40 -0
- package/dist/services/external-mounts.js +187 -0
- package/dist/services/external-mounts.js.map +1 -0
- package/dist/services/files-manager.d.ts +252 -0
- package/dist/services/files-manager.js +1075 -0
- package/dist/services/files-manager.js.map +1 -0
- package/dist/services/files-mounts.d.ts +42 -0
- package/dist/services/files-mounts.js +207 -0
- package/dist/services/files-mounts.js.map +1 -0
- package/dist/services/instance-manager.d.ts +13 -0
- package/dist/services/instance-manager.js +138 -46
- package/dist/services/instance-manager.js.map +1 -1
- package/dist/services/llm-proxy/index.d.ts +16 -2
- package/dist/services/llm-proxy/index.js +48 -44
- package/dist/services/llm-proxy/index.js.map +1 -1
- package/dist/services/llm-proxy/probe.d.ts +6 -0
- package/dist/services/llm-proxy/probe.js +85 -0
- package/dist/services/llm-proxy/probe.js.map +1 -0
- package/dist/services/llm-proxy/ssrf.d.ts +1 -0
- package/dist/services/llm-proxy/ssrf.js +24 -9
- package/dist/services/llm-proxy/ssrf.js.map +1 -1
- package/dist/services/nomad-manager.d.ts +4 -0
- package/dist/services/nomad-manager.js +428 -35
- package/dist/services/nomad-manager.js.map +1 -1
- package/dist/services/organize/applier.d.ts +46 -0
- package/dist/services/organize/applier.js +218 -0
- package/dist/services/organize/applier.js.map +1 -0
- package/dist/services/organize/rules.d.ts +57 -0
- package/dist/services/organize/rules.js +286 -0
- package/dist/services/organize/rules.js.map +1 -0
- package/dist/services/organize/scanner.d.ts +50 -0
- package/dist/services/organize/scanner.js +366 -0
- package/dist/services/organize/scanner.js.map +1 -0
- package/dist/services/organize/store.d.ts +14 -0
- package/dist/services/organize/store.js +82 -0
- package/dist/services/organize/store.js.map +1 -0
- package/dist/services/panel-manager.js +20 -1
- package/dist/services/panel-manager.js.map +1 -1
- package/dist/services/process-manager.js +4 -3
- package/dist/services/process-manager.js.map +1 -1
- package/dist/services/runtime/adapters/hermes.d.ts +30 -1
- package/dist/services/runtime/adapters/hermes.js +219 -6
- package/dist/services/runtime/adapters/hermes.js.map +1 -1
- package/dist/services/runtime/adapters/openclaw-mcporter.d.ts +45 -0
- package/dist/services/runtime/adapters/openclaw-mcporter.js +108 -0
- package/dist/services/runtime/adapters/openclaw-mcporter.js.map +1 -0
- package/dist/services/runtime/adapters/openclaw-routes.d.ts +8 -2
- package/dist/services/runtime/adapters/openclaw-routes.js +68 -0
- package/dist/services/runtime/adapters/openclaw-routes.js.map +1 -1
- package/dist/services/runtime/adapters/openclaw.d.ts +177 -0
- package/dist/services/runtime/adapters/openclaw.js +1171 -11
- package/dist/services/runtime/adapters/openclaw.js.map +1 -1
- package/dist/services/runtime/instance.d.ts +1 -1
- package/dist/services/runtime/instance.js +1 -1
- package/dist/services/runtime/instance.js.map +1 -1
- package/dist/services/runtime/mcp-shims/anythingllm-shim.d.ts +46 -0
- package/dist/services/runtime/mcp-shims/anythingllm-shim.js +281 -0
- package/dist/services/runtime/mcp-shims/anythingllm-shim.js.map +1 -0
- package/dist/services/runtime/mcp-shims/drive-shim.d.ts +54 -0
- package/dist/services/runtime/mcp-shims/drive-shim.js +489 -0
- package/dist/services/runtime/mcp-shims/drive-shim.js.map +1 -0
- package/dist/services/runtime/mcp-shims/firewall.d.ts +26 -0
- package/dist/services/runtime/mcp-shims/firewall.js +129 -0
- package/dist/services/runtime/mcp-shims/firewall.js.map +1 -0
- package/dist/services/runtime/mcp-shims/searxng-shim.d.ts +27 -0
- package/dist/services/runtime/mcp-shims/searxng-shim.js +125 -0
- package/dist/services/runtime/mcp-shims/searxng-shim.js.map +1 -0
- package/dist/services/runtime/mcp-shims/write-mcp-entry.d.ts +83 -0
- package/dist/services/runtime/mcp-shims/write-mcp-entry.js +127 -0
- package/dist/services/runtime/mcp-shims/write-mcp-entry.js.map +1 -0
- package/dist/services/runtime/migrations.d.ts +8 -0
- package/dist/services/runtime/migrations.js +100 -0
- package/dist/services/runtime/migrations.js.map +1 -1
- package/dist/services/runtime/types.d.ts +46 -0
- package/dist/services/setup-manager.js +99 -24
- package/dist/services/setup-manager.js.map +1 -1
- package/dist/services/suggestions.d.ts +27 -0
- package/dist/services/suggestions.js +133 -0
- package/dist/services/suggestions.js.map +1 -0
- package/dist/services/task-registry.js +4 -2
- package/dist/services/task-registry.js.map +1 -1
- package/dist/services/telemetry/device-fingerprint.d.ts +1 -1
- package/dist/services/telemetry/device-fingerprint.js +1 -1
- package/dist/services/types-shim.d.ts +16 -0
- package/dist/services/types-shim.js +2 -0
- package/dist/services/types-shim.js.map +1 -0
- package/dist/services/webdav/server.d.ts +24 -0
- package/dist/services/webdav/server.js +420 -0
- package/dist/services/webdav/server.js.map +1 -0
- package/dist/services/webdav/xml-builder.d.ts +73 -0
- package/dist/services/webdav/xml-builder.js +156 -0
- package/dist/services/webdav/xml-builder.js.map +1 -0
- package/dist/services/workspace-builder.d.ts +29 -0
- package/dist/services/workspace-builder.js +188 -0
- package/dist/services/workspace-builder.js.map +1 -0
- package/dist/types.d.ts +231 -1
- package/dist/utils/instance-lock.d.ts +22 -0
- package/dist/utils/instance-lock.js +48 -0
- package/dist/utils/instance-lock.js.map +1 -0
- package/dist/utils/path-locks.d.ts +30 -0
- package/dist/utils/path-locks.js +63 -0
- package/dist/utils/path-locks.js.map +1 -0
- package/dist/utils/path-safety.d.ts +41 -0
- package/dist/utils/path-safety.js +119 -0
- package/dist/utils/path-safety.js.map +1 -0
- package/dist/utils/safe-json.js +55 -22
- package/dist/utils/safe-json.js.map +1 -1
- package/dist/utils/safe-write.d.ts +24 -0
- package/dist/utils/safe-write.js +82 -0
- package/dist/utils/safe-write.js.map +1 -0
- package/install/jishu-install.sh +323 -27
- package/install/jishu-uninstall.sh +353 -20
- package/package.json +18 -1
- package/public/assets/Dashboard-BdWPtroF.js +1 -0
- package/public/assets/{HermesChatPanel-mFSureyc.js → HermesChatPanel-B_2HlVBQ.js} +1 -1
- package/public/assets/HermesConfigForm-DVlhg3WV.js +4 -0
- package/public/assets/{InitPassword-CVA8wQA6.js → InitPassword-D7glTExX.js} +1 -1
- package/public/assets/InstanceDetail-CxSy2cpe.js +92 -0
- package/public/assets/{Login-BWsZH2mu.js → Login-Cfr5c2sv.js} +1 -1
- package/public/assets/NewInstance-BIYDmJis.js +1 -0
- package/public/assets/ProviderRecommendations-BuRnvRcI.js +1 -0
- package/public/assets/Settings-Cc-tYBil.js +1 -0
- package/public/assets/Setup-lGZEk5jq.js +1 -0
- package/public/assets/{WeixinLoginPanel-CnjR8xMu.js → WeixinLoginPanel-CoGqzxeV.js} +2 -2
- package/public/assets/index-87IJXG-w.css +1 -0
- package/public/assets/index-BZc5zH7u.js +19 -0
- package/public/assets/providers-DtNXh9JD.js +1 -0
- package/public/assets/registry-BWnkJgZ1.js +2 -0
- package/public/assets/{usePolling-Do5Erqm_.js → usePolling-CwwT9KrC.js} +1 -1
- package/public/assets/{vendor-i18n-ucpM0OR0.js → vendor-i18n-y9V7Sfuu.js} +1 -1
- package/public/assets/{vendor-react-Bk1hRGiY.js → vendor-react-BWrEVJVb.js} +6 -6
- package/public/index.html +4 -4
- package/scripts/check-app-spec.mjs +457 -0
- package/scripts/check-i18n.mjs +154 -0
- package/scripts/check-new-file-tests.mjs +230 -0
- package/scripts/check-quarantine-expiry.mjs +105 -0
- package/scripts/perf/README.md +49 -0
- package/scripts/perf/auth.js +99 -0
- package/scripts/perf/config.js +63 -0
- package/scripts/perf/instances.js +143 -0
- package/scripts/perf/proxy.js +96 -0
- package/scripts/run.sh +4 -4
- package/scripts/smoke/files-w1.sh +142 -0
- package/scripts/smoke-backend.mjs +122 -0
- package/scripts/smoke-post-publish.mjs +346 -0
- package/public/assets/Dashboard-B-JoOjBQ.js +0 -1
- package/public/assets/HermesConfigForm-DvR05LK1.js +0 -4
- package/public/assets/InstanceDetail-DcZW2QGO.js +0 -91
- package/public/assets/NewInstance-BCIrAd86.js +0 -1
- package/public/assets/Settings-xkDcduFz.js +0 -1
- package/public/assets/Setup-Cfuwj4gV.js +0 -1
- package/public/assets/index-CPhVFEsx.css +0 -1
- package/public/assets/index-DQsM6Joa.js +0 -19
- package/public/assets/providers-V-vwrExZ.js +0 -1
- package/public/assets/registry-B4UFJdpA.js +0 -2
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
id: weknora-container
|
|
2
|
+
name: WeKnora
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
jishushell:
|
|
5
|
+
min_version: "0.5.15"
|
|
6
|
+
description: "Tencent 开源 RAG 知识库:文档解析 + 向量检索 + 对话式问答(5 容器单 Nomad TaskGroup)"
|
|
7
|
+
singleInstance: true
|
|
8
|
+
|
|
9
|
+
# 拓扑(每个 task 独立 docker netns,跨 task 经 host.docker.internal:<host-port>):
|
|
10
|
+
# 注:原始设计想用 Nomad group bridge mode 让 task 共享 netns 走 127.0.0.1,
|
|
11
|
+
# 但需要 /opt/cni/bin/ 下的 containernetworking 插件(Pi 默认没装)。退而用
|
|
12
|
+
# host 端口发布 + host.docker.internal 路由(panel `extra_hosts:host-gateway`
|
|
13
|
+
# 已 mapping 到 172.17.0.1 = docker0 bridge gateway)。
|
|
14
|
+
# 关键:5 个发布端口都设 `host_network: docker_bridge` 让 panel/Nomad 把
|
|
15
|
+
# docker-proxy 绑到 docker0 IP(172.17.0.1),peer 容器走 host.docker.internal
|
|
16
|
+
# 即 172.17.0.1 时打得通。要求 nomad.hcl 声明 `host_network "docker_bridge"
|
|
17
|
+
# { cidr = "172.17.0.0/16" }`(panel doctor 会自动写入)。
|
|
18
|
+
#
|
|
19
|
+
# weknora-ui (nginx :80) ─proxy_pass─► weknora-app :8080
|
|
20
|
+
# │ │
|
|
21
|
+
# │ ├─► paradedb :5432 (PG17 + pgvector + FTS5;同时承担 vector store)
|
|
22
|
+
# │ ├─► redis :6379 (任务队列 / 流式响应缓存)
|
|
23
|
+
# │ └─► docreader:50051 (gRPC:PDF / Word / Markdown 解析)
|
|
24
|
+
# │
|
|
25
|
+
# host :18092 ◄── published (visibility: external)
|
|
26
|
+
#
|
|
27
|
+
# 关键裁剪(相对官方 docker-compose):
|
|
28
|
+
# - RETRIEVE_DRIVER=postgres:复用 paradedb 的 pgvector,**不装 Qdrant / Milvus / Weaviate**(省 1 task + ~70MB 下载)
|
|
29
|
+
# - WEKNORA_SANDBOX_MODE=disabled:不装 weknora-sandbox(省 ~80MB;阉割 agent 工具沙箱执行,RAG 主流程不受影响)
|
|
30
|
+
# - 不装 Neo4j / MinIO / Langfuse / Jaeger / SearXNG / Doris / ClickHouse / Dex(都是 profile 才启的)
|
|
31
|
+
#
|
|
32
|
+
# 镜像总下载:~2.8 GB (arm64) / ~3.0 GB (amd64);解压磁盘 ~6.5 GB
|
|
33
|
+
# 稳态内存预算:~1.5–2.0 GB(4 GB Pi 紧张,建议 8 GB Pi 5 / x86 节点)
|
|
34
|
+
|
|
35
|
+
tasks:
|
|
36
|
+
# ────────────────────────────────────────────────────────────────
|
|
37
|
+
# 1) PostgreSQL(ParadeDB:PG17 + pgvector + ParadeDB BM25 全文)
|
|
38
|
+
# role: sidecar → prestart 起来并持续运行;service tasks 在所有
|
|
39
|
+
# sidecar Running 后再启动(但 Running ≠ Ready,weknora-app 自己
|
|
40
|
+
# 要靠连接重试兜住 DB init 的 30s 窗口)。
|
|
41
|
+
# ────────────────────────────────────────────────────────────────
|
|
42
|
+
- name: paradedb
|
|
43
|
+
role: sidecar
|
|
44
|
+
runtime: container
|
|
45
|
+
image: "paradedb/paradedb:v0.22.2-pg17"
|
|
46
|
+
user: "host"
|
|
47
|
+
env:
|
|
48
|
+
POSTGRES_USER: "weknora"
|
|
49
|
+
# ⚠️ 开发默认值。生产请改:
|
|
50
|
+
# docker exec -it <container> psql -U weknora -c "ALTER USER weknora WITH PASSWORD '<new>'"
|
|
51
|
+
# 并同步更新 weknora-app 的 DB_PASSWORD。
|
|
52
|
+
POSTGRES_PASSWORD: "weknora-dev-pg-pw"
|
|
53
|
+
POSTGRES_DB: "WeKnora"
|
|
54
|
+
PGDATA: "/var/lib/postgresql/data/pgdata"
|
|
55
|
+
resources:
|
|
56
|
+
cpu: "500m"
|
|
57
|
+
memory: "384Mi"
|
|
58
|
+
ports:
|
|
59
|
+
- name: pg
|
|
60
|
+
port: 18093
|
|
61
|
+
container_port: 5432
|
|
62
|
+
visibility: external
|
|
63
|
+
host_network: docker_bridge
|
|
64
|
+
volumes:
|
|
65
|
+
- source: "~/.jishushell/apps/${app_id}/postgres"
|
|
66
|
+
target: "/var/lib/postgresql/data"
|
|
67
|
+
|
|
68
|
+
# ────────────────────────────────────────────────────────────────
|
|
69
|
+
# 2) Redis(任务队列 + Stream Manager 状态)
|
|
70
|
+
# ────────────────────────────────────────────────────────────────
|
|
71
|
+
- name: redis
|
|
72
|
+
role: sidecar
|
|
73
|
+
runtime: container
|
|
74
|
+
image: "redis:7.0-alpine"
|
|
75
|
+
user: "host"
|
|
76
|
+
args:
|
|
77
|
+
- "redis-server"
|
|
78
|
+
- "--appendonly"
|
|
79
|
+
- "yes"
|
|
80
|
+
# 无密码:仅 group 内 127.0.0.1 可达,未发布到宿主。
|
|
81
|
+
# 如需密码,加 --requirepass <pw> 并同步设 weknora-app 的 REDIS_PASSWORD。
|
|
82
|
+
resources:
|
|
83
|
+
cpu: "100m"
|
|
84
|
+
memory: "96Mi"
|
|
85
|
+
ports:
|
|
86
|
+
- name: redis
|
|
87
|
+
port: 18094
|
|
88
|
+
container_port: 6379
|
|
89
|
+
visibility: external
|
|
90
|
+
host_network: docker_bridge
|
|
91
|
+
volumes:
|
|
92
|
+
- source: "~/.jishushell/apps/${app_id}/redis"
|
|
93
|
+
target: "/data"
|
|
94
|
+
|
|
95
|
+
# ────────────────────────────────────────────────────────────────
|
|
96
|
+
# 3) docreader(最大头:1.7 GB;Python + markitdown + PDF 渲染)
|
|
97
|
+
# weknora-app `depends_on: docreader (service_healthy)` 是 core
|
|
98
|
+
# 依赖,**不能砍**。
|
|
99
|
+
# ────────────────────────────────────────────────────────────────
|
|
100
|
+
- name: docreader
|
|
101
|
+
role: sidecar
|
|
102
|
+
runtime: container
|
|
103
|
+
image: "wechatopenai/weknora-docreader:latest"
|
|
104
|
+
# Image entrypoint uses `uv` package manager which writes to /.cache/uv;
|
|
105
|
+
# forcing host uid (1000:1000) gets Permission denied on the root-owned
|
|
106
|
+
# filesystem and the container exits 2. Run as root — image is read-only
|
|
107
|
+
# in the layers that matter; runtime writes go to /tmp/docreader (bind
|
|
108
|
+
# mount, panel-user owned).
|
|
109
|
+
user: "0:0"
|
|
110
|
+
env:
|
|
111
|
+
DOCREADER_IMAGE_OUTPUT_DIR: "/tmp/docreader"
|
|
112
|
+
MAX_FILE_SIZE_MB: "50"
|
|
113
|
+
DOCREADER_MARKITDOWN_MAX_WORKERS: "1"
|
|
114
|
+
DOCREADER_PDF_RENDER_MAX_WORKERS: "1"
|
|
115
|
+
DOCREADER_PDF_RENDER_DPI: "200"
|
|
116
|
+
DOCREADER_PDF_JPEG_QUALITY: "90"
|
|
117
|
+
resources:
|
|
118
|
+
cpu: "800m"
|
|
119
|
+
memory: "768Mi"
|
|
120
|
+
ports:
|
|
121
|
+
- name: grpc
|
|
122
|
+
port: 18095
|
|
123
|
+
container_port: 50051
|
|
124
|
+
visibility: external
|
|
125
|
+
host_network: docker_bridge
|
|
126
|
+
volumes:
|
|
127
|
+
# tmp 目录与 weknora-app 共享,docreader 写 / app 读(图片提取场景)。
|
|
128
|
+
- source: "~/.jishushell/apps/${app_id}/docreader-tmp"
|
|
129
|
+
target: "/tmp/docreader"
|
|
130
|
+
|
|
131
|
+
# ────────────────────────────────────────────────────────────────
|
|
132
|
+
# 4) WeKnora 后端 app(Go)
|
|
133
|
+
# ────────────────────────────────────────────────────────────────
|
|
134
|
+
- name: app
|
|
135
|
+
role: service
|
|
136
|
+
runtime: container
|
|
137
|
+
image: "wechatopenai/weknora-app:latest"
|
|
138
|
+
# Image entrypoint chowns /app/skills/preloaded; with host uid the
|
|
139
|
+
# chown fails with "Operation not permitted" and the container crashes.
|
|
140
|
+
# Bind-mounted /data/files is panel-user owned (1000) — Go binary opens
|
|
141
|
+
# it as root without issue.
|
|
142
|
+
user: "0:0"
|
|
143
|
+
# panel default is cap_drop:ALL; weknora-app entrypoint does
|
|
144
|
+
# `chown -R appuser /app/skills/preloaded` at startup → needs CHOWN.
|
|
145
|
+
# It then `su appuser` to drop to non-root → needs SETUID/SETGID/SETPCAP.
|
|
146
|
+
cap_add: ["CHOWN", "FOWNER", "DAC_OVERRIDE", "SETUID", "SETGID", "SETPCAP"]
|
|
147
|
+
env:
|
|
148
|
+
# ── 基础运行时 ──
|
|
149
|
+
GIN_MODE: "release"
|
|
150
|
+
LOG_LEVEL: "info"
|
|
151
|
+
TZ: "Asia/Shanghai"
|
|
152
|
+
WEKNORA_LANGUAGE: "zh-CN"
|
|
153
|
+
DISABLE_REGISTRATION: "false"
|
|
154
|
+
|
|
155
|
+
# ── 数据库(host_network: docker_bridge → 绑 docker0 IP,
|
|
156
|
+
# host.docker.internal=172.17.0.1 容器内可达) ──
|
|
157
|
+
DB_DRIVER: "postgres"
|
|
158
|
+
DB_HOST: "host.docker.internal"
|
|
159
|
+
DB_PORT: "18093"
|
|
160
|
+
DB_USER: "weknora"
|
|
161
|
+
DB_PASSWORD: "weknora-dev-pg-pw"
|
|
162
|
+
DB_NAME: "WeKnora"
|
|
163
|
+
|
|
164
|
+
# ── 向量检索:走 paradedb 的 pgvector(砍 Qdrant / Milvus / Weaviate) ──
|
|
165
|
+
RETRIEVE_DRIVER: "postgres"
|
|
166
|
+
|
|
167
|
+
# ── Redis ──
|
|
168
|
+
REDIS_ADDR: "host.docker.internal:18094"
|
|
169
|
+
REDIS_DB: "0"
|
|
170
|
+
REDIS_PREFIX: "stream:"
|
|
171
|
+
STREAM_MANAGER_TYPE: "redis"
|
|
172
|
+
|
|
173
|
+
# ── docreader gRPC ──
|
|
174
|
+
DOCREADER_ADDR: "host.docker.internal:18095"
|
|
175
|
+
DOCREADER_TRANSPORT: "grpc"
|
|
176
|
+
|
|
177
|
+
# ── 文件存储:local 模式,写到 bind-mounted /data/files ──
|
|
178
|
+
STORAGE_TYPE: "local"
|
|
179
|
+
LOCAL_STORAGE_BASE_DIR: "/data/files"
|
|
180
|
+
MAX_FILE_SIZE_MB: "50"
|
|
181
|
+
|
|
182
|
+
# ── Sandbox:关。Agent 工具调用执行能力不可用,RAG 主流程不受影响。 ──
|
|
183
|
+
# 如需开启:(a) 把 weknora-sandbox 也加进 tasks[];(b) 改为
|
|
184
|
+
# WEKNORA_SANDBOX_MODE=docker;(c) 注意 sandbox 需要 docker.sock 挂载,
|
|
185
|
+
# Nomad 调度的容器内不一定有权限——可能需要额外设计。
|
|
186
|
+
WEKNORA_SANDBOX_MODE: "disabled"
|
|
187
|
+
|
|
188
|
+
# ── 安全密钥 ⚠️ 开发默认值,生产前必改 ──
|
|
189
|
+
# 推荐:openssl rand -hex 32 重新生成;同步改前端 NEXTAUTH_SECRET 之类。
|
|
190
|
+
JWT_SECRET: "weknora-dev-jwt-secret-CHANGE-ME-IN-PROD"
|
|
191
|
+
TENANT_AES_KEY: "weknora-dev-tenant-key-32bytes!!"
|
|
192
|
+
SYSTEM_AES_KEY: "weknora-dev-system-key-32bytes!!"
|
|
193
|
+
|
|
194
|
+
# ── LLM provider:留空,让用户在 WeKnora UI 里手动配。 ──
|
|
195
|
+
# 想用 panel 内嵌 LLM proxy(推荐)的话,UI 里填:
|
|
196
|
+
# base_url: http://host.docker.internal:8090/proxy/v1
|
|
197
|
+
# api_key: <从 panel /instances/:id 拿到的 sk-js-*>
|
|
198
|
+
# 这样审计 / 限流 / 上游切换都收敛在 panel 一层。
|
|
199
|
+
OLLAMA_BASE_URL: ""
|
|
200
|
+
|
|
201
|
+
# ── Tracing / Observability:全关 ──
|
|
202
|
+
LANGFUSE_ENABLED: "false"
|
|
203
|
+
OTEL_TRACES_EXPORTER: "none"
|
|
204
|
+
OTEL_METRICS_EXPORTER: "none"
|
|
205
|
+
OTEL_LOGS_EXPORTER: "none"
|
|
206
|
+
ENABLE_GRAPH_RAG: "false"
|
|
207
|
+
NEO4J_ENABLE: "false"
|
|
208
|
+
|
|
209
|
+
# ── 并发 ──
|
|
210
|
+
CONCURRENCY_POOL_SIZE: "5"
|
|
211
|
+
|
|
212
|
+
# ── SSRF 白名单:默认走 panel 反代,无需对外。 ──
|
|
213
|
+
SSRF_WHITELIST: ""
|
|
214
|
+
SSRF_WHITELIST_EXTRA: ""
|
|
215
|
+
resources:
|
|
216
|
+
cpu: "600m"
|
|
217
|
+
memory: "640Mi"
|
|
218
|
+
ports:
|
|
219
|
+
- name: http
|
|
220
|
+
# Published on docker0 so the ui task can reach backend via
|
|
221
|
+
# host.docker.internal:18096 (no shared netns on Pi w/o CNI).
|
|
222
|
+
port: 18096
|
|
223
|
+
container_port: 8080
|
|
224
|
+
visibility: external
|
|
225
|
+
host_network: docker_bridge
|
|
226
|
+
volumes:
|
|
227
|
+
# 知识库附件落盘。与 ~/.jishushell/files/ 故意分离 ——
|
|
228
|
+
# WeKnora 自己管文件元数据 / RAG 索引,避免与 panel W3 FTS5 打架。
|
|
229
|
+
- source: "~/.jishushell/apps/${app_id}/files"
|
|
230
|
+
target: "/data/files"
|
|
231
|
+
|
|
232
|
+
# ────────────────────────────────────────────────────────────────
|
|
233
|
+
# 5) WeKnora 前端 UI(nginx + Vue dist)
|
|
234
|
+
# ⚠️ 镜像不支持 BASE_URL 子路径 → 不走 panel /apps/weknora/* 反代,
|
|
235
|
+
# 用端口直连 18092。子路径要走需 fork 镜像(vite.config base + nginx
|
|
236
|
+
# location prefix),暂列为后续工作。
|
|
237
|
+
# ────────────────────────────────────────────────────────────────
|
|
238
|
+
- name: ui
|
|
239
|
+
role: service
|
|
240
|
+
runtime: container
|
|
241
|
+
image: "wechatopenai/weknora-ui:latest"
|
|
242
|
+
# nginx entrypoint needs to mkdir /var/cache/nginx/client_temp and
|
|
243
|
+
# rewrite /etc/nginx/conf.d/default.conf via envsubst — both require root.
|
|
244
|
+
# Host uid breaks both steps and the container crashloops.
|
|
245
|
+
user: "0:0"
|
|
246
|
+
# nginx master spawns workers as uid 101 (nginx user) and chowns the
|
|
247
|
+
# cache dir to it — needs CHOWN to do so even when master is root.
|
|
248
|
+
cap_add: ["CHOWN", "SETGID", "SETUID"]
|
|
249
|
+
env:
|
|
250
|
+
MAX_FILE_SIZE_MB: "50"
|
|
251
|
+
APP_HOST: "host.docker.internal"
|
|
252
|
+
APP_PORT: "18096"
|
|
253
|
+
APP_SCHEME: "http"
|
|
254
|
+
resources:
|
|
255
|
+
cpu: "100m"
|
|
256
|
+
memory: "48Mi"
|
|
257
|
+
ports:
|
|
258
|
+
- name: http
|
|
259
|
+
# 18092 在 panel 18xxx 范围内(filebrowser 18088 / openclaw-gw 18790)。
|
|
260
|
+
# 避开 OpenWebUI 3000、SearXNG 8080、panel 8090。
|
|
261
|
+
port: 18092
|
|
262
|
+
container_port: 80
|
|
263
|
+
visibility: external
|
|
264
|
+
volumes:
|
|
265
|
+
# 覆盖镜像内的 nginx 模板:上游模板带 `add_header X-Frame-Options
|
|
266
|
+
# SAMEORIGIN`,panel 实例详情页 iframe (origin=panel:8090) 嵌
|
|
267
|
+
# WeKnora UI (origin=panel:18092) 会被浏览器拒绝。我们这份完全
|
|
268
|
+
# 照搬上游 location 块,只去掉 X-Frame-Options/CSP 限制,保留
|
|
269
|
+
# ${APP_HOST}/${APP_PORT}/${APP_SCHEME}/${MAX_FILE_SIZE} 由镜像
|
|
270
|
+
# docker-entrypoint.sh envsubst。
|
|
271
|
+
- source: "~/.jishushell/apps/${app_id}/nginx/default.conf.template"
|
|
272
|
+
target: "/etc/nginx/templates/default.conf.template"
|
|
273
|
+
readonly: true
|
|
274
|
+
health:
|
|
275
|
+
http:
|
|
276
|
+
path: /
|
|
277
|
+
port: 80
|
|
278
|
+
interval: "15s"
|
|
279
|
+
timeout: "5s"
|
|
280
|
+
retries: 6
|
|
281
|
+
start_period: "60s" # 等 paradedb 初始化 + weknora-app boot + nginx 上线
|
|
282
|
+
|
|
283
|
+
provides:
|
|
284
|
+
# ⚠️ Capability 命名特意避开 `web-<slug>` 和 `<slug>-ui` 这两种 panel 顶层
|
|
285
|
+
# `/apps/<slug>/*` 反代会命中的模式(详见 src/server.ts:69 resolveAppSlugUpstream)。
|
|
286
|
+
# 原因:panel 反代**不去掉** `/apps/<slug>` 前缀(server.ts:382 注释),原样
|
|
287
|
+
# 转发给上游;而 WeKnora 前端镜像的 nginx 只 listen `/`、vite build 也没设
|
|
288
|
+
# `base`——任何 `/apps/weknora/*` 请求到了 ui 容器都会 404。
|
|
289
|
+
#
|
|
290
|
+
# 因此用 `knowledge-weknora`(业务命名、非通配前缀),让它**只**进 capability
|
|
291
|
+
# registry 供未来 agent `requires:` 注入使用,**不**触发 panel 顶层反代。
|
|
292
|
+
# 用户访问 UI 走端口直连:http://<host>:18092/。
|
|
293
|
+
- capability: "knowledge-weknora"
|
|
294
|
+
task: "ui"
|
|
295
|
+
port: 18092
|
|
296
|
+
path: "/"
|
|
297
|
+
protocol: "http"
|
|
298
|
+
visibility: "external"
|
|
299
|
+
# 强制 iframe 走 panel 反代而不是端口直连。原因:
|
|
300
|
+
# 1. 用户从 panel 8090 同源加载详情页,corp 防火墙 / VPN / 混合内容
|
|
301
|
+
# 策略可能让 8090 通但 18092 不通(典型:"refused to connect")。
|
|
302
|
+
# 2. nginx SAMEORIGIN/CSP 即便去掉了,浏览器 Private Network Access
|
|
303
|
+
# 仍可能拒绝跨网段 iframe;走 panel 反代后 iframe src 与 panel
|
|
304
|
+
# 同源,绕开所有边界检查。
|
|
305
|
+
# 3. WeKnora 前端 vite build 没设 `base`,绝对路径 `/api/...` 直接
|
|
306
|
+
# 打到 panel API → 但反代会把 base 改成 /api/instances/.../provides/
|
|
307
|
+
# /knowledge-weknora/,后续相对 fetch 正确(panel 反代有 base 重写)。
|
|
308
|
+
# Direct mode: iframe loads http://<host>:18092/ instead of the panel
|
|
309
|
+
# capability proxy. The proxy path approach kept tripping over WeKnora's
|
|
310
|
+
# axios 401 interceptor, which hard-codes `window.location.href = '/login'`
|
|
311
|
+
# and bounces the iframe to the panel root login page. Rewriting that
|
|
312
|
+
# literal at the proxy works for the assignment but the bundle has
|
|
313
|
+
# additional indirect navigations (vue-router fallback when pushState is
|
|
314
|
+
# blocked, internal pinia store actions) that aren't reachable from a
|
|
315
|
+
# text rewrite — same-origin iframe vs. direct iframe tradeoff favors
|
|
316
|
+
# direct here because the dev/internal browser already reaches 18092
|
|
317
|
+
# successfully (popout link confirmed working).
|
|
318
|
+
embedded: "direct"
|
|
319
|
+
description: "WeKnora 知识库 Web UI"
|
|
320
|
+
|
|
321
|
+
lifecycle:
|
|
322
|
+
install:
|
|
323
|
+
# 镜像按尺寸升序下载:小的先稳,大的(docreader 1.7GB)放最后,
|
|
324
|
+
# 万一失败下次重试时小的全部 skip,从大的断点续。
|
|
325
|
+
- downloadImage: "redis:7.0-alpine"
|
|
326
|
+
- downloadImage: "wechatopenai/weknora-ui:latest"
|
|
327
|
+
- downloadImage: "wechatopenai/weknora-app:latest"
|
|
328
|
+
- downloadImage: "paradedb/paradedb:v0.22.2-pg17"
|
|
329
|
+
# docreader 单独给 30 分钟总超时 + 30 分钟 idle 超时(1.7GB 内含 400MB+ 的
|
|
330
|
+
# 大 layer;Pi 带宽 ~2 MB/s 下单 layer 下载耗时可超 panel 默认 180s idle
|
|
331
|
+
# → 误判 stalled。设 30 分钟 idle 给慢网充足时间)。
|
|
332
|
+
- downloadImage: "wechatopenai/weknora-docreader:latest"
|
|
333
|
+
timeout_ms: 1800000
|
|
334
|
+
idle_timeout_ms: 1800000
|
|
335
|
+
|
|
336
|
+
- mkdir: "~/.jishushell/apps/${app_id}/postgres"
|
|
337
|
+
- mkdir: "~/.jishushell/apps/${app_id}/redis"
|
|
338
|
+
- mkdir: "~/.jishushell/apps/${app_id}/docreader-tmp"
|
|
339
|
+
- mkdir: "~/.jishushell/apps/${app_id}/files"
|
|
340
|
+
- mkdir: "~/.jishushell/apps/${app_id}/nginx"
|
|
341
|
+
# 写自定义 nginx 模板(去掉 X-Frame-Options 让 panel 实例详情页能
|
|
342
|
+
# iframe 嵌入)。单引号 heredoc 关掉 shell 展开,保留 ${APP_HOST}/
|
|
343
|
+
# ${APP_PORT}/${APP_SCHEME}/${MAX_FILE_SIZE} 由镜像 envsubst 替换。
|
|
344
|
+
- run: |
|
|
345
|
+
set -e
|
|
346
|
+
TPL="$HOME/.jishushell/apps/${app_id}/nginx/default.conf.template"
|
|
347
|
+
cat > "$TPL" <<'NGX'
|
|
348
|
+
server {
|
|
349
|
+
listen 80;
|
|
350
|
+
server_name localhost;
|
|
351
|
+
|
|
352
|
+
client_max_body_size ${MAX_FILE_SIZE};
|
|
353
|
+
|
|
354
|
+
gzip on;
|
|
355
|
+
gzip_min_length 1024;
|
|
356
|
+
gzip_comp_level 6;
|
|
357
|
+
gzip_types text/plain text/css application/json application/javascript
|
|
358
|
+
application/xml text/xml application/x-javascript;
|
|
359
|
+
|
|
360
|
+
# X-Frame-Options 故意不发:panel 实例详情页 (origin=panel:8090)
|
|
361
|
+
# 要 iframe 嵌入本服务 (origin=panel:18092),原版 SAMEORIGIN
|
|
362
|
+
# 会被浏览器拒。其余安全头照旧。
|
|
363
|
+
add_header X-Content-Type-Options nosniff always;
|
|
364
|
+
add_header Referrer-Policy strict-origin-when-cross-origin always;
|
|
365
|
+
|
|
366
|
+
location /assets/ {
|
|
367
|
+
root /usr/share/nginx/html;
|
|
368
|
+
expires max;
|
|
369
|
+
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
location = /files {
|
|
373
|
+
proxy_pass ${APP_SCHEME}://${APP_HOST}:${APP_PORT}/files;
|
|
374
|
+
proxy_http_version 1.1;
|
|
375
|
+
proxy_set_header Host $host;
|
|
376
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
377
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
location /api/ {
|
|
381
|
+
proxy_pass ${APP_SCHEME}://${APP_HOST}:${APP_PORT}/api/;
|
|
382
|
+
proxy_http_version 1.1;
|
|
383
|
+
proxy_set_header Host $host;
|
|
384
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
385
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
386
|
+
proxy_set_header Connection "";
|
|
387
|
+
proxy_buffering off;
|
|
388
|
+
proxy_cache off;
|
|
389
|
+
proxy_read_timeout 3600s;
|
|
390
|
+
proxy_send_timeout 3600s;
|
|
391
|
+
proxy_connect_timeout 30s;
|
|
392
|
+
proxy_next_upstream error timeout http_502 http_503 http_504;
|
|
393
|
+
proxy_next_upstream_tries 3;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
location / {
|
|
397
|
+
root /usr/share/nginx/html;
|
|
398
|
+
try_files $uri $uri/ /index.html;
|
|
399
|
+
add_header Cache-Control "no-cache, must-revalidate";
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
NGX
|
|
403
|
+
chmod 644 "$TPL"
|
|
404
|
+
echo "[install] wrote nginx template (no X-Frame-Options) → $TPL"
|
|
405
|
+
|
|
406
|
+
pre_start:
|
|
407
|
+
# 幂等:start/restart 都过这条路径,目录被误删可自愈。
|
|
408
|
+
- mkdir: "~/.jishushell/apps/${app_id}/postgres"
|
|
409
|
+
- mkdir: "~/.jishushell/apps/${app_id}/redis"
|
|
410
|
+
- mkdir: "~/.jishushell/apps/${app_id}/docreader-tmp"
|
|
411
|
+
- mkdir: "~/.jishushell/apps/${app_id}/files"
|
|
412
|
+
- mkdir: "~/.jishushell/apps/${app_id}/nginx"
|
|
413
|
+
- run: |
|
|
414
|
+
set -e
|
|
415
|
+
TPL="$HOME/.jishushell/apps/${app_id}/nginx/default.conf.template"
|
|
416
|
+
if [ ! -f "$TPL" ]; then
|
|
417
|
+
cat > "$TPL" <<'NGX'
|
|
418
|
+
server {
|
|
419
|
+
listen 80;
|
|
420
|
+
server_name localhost;
|
|
421
|
+
client_max_body_size ${MAX_FILE_SIZE};
|
|
422
|
+
gzip on;
|
|
423
|
+
gzip_min_length 1024;
|
|
424
|
+
gzip_comp_level 6;
|
|
425
|
+
gzip_types text/plain text/css application/json application/javascript
|
|
426
|
+
application/xml text/xml application/x-javascript;
|
|
427
|
+
add_header X-Content-Type-Options nosniff always;
|
|
428
|
+
add_header Referrer-Policy strict-origin-when-cross-origin always;
|
|
429
|
+
location /assets/ {
|
|
430
|
+
root /usr/share/nginx/html;
|
|
431
|
+
expires max;
|
|
432
|
+
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
433
|
+
}
|
|
434
|
+
location = /files {
|
|
435
|
+
proxy_pass ${APP_SCHEME}://${APP_HOST}:${APP_PORT}/files;
|
|
436
|
+
proxy_http_version 1.1;
|
|
437
|
+
proxy_set_header Host $host;
|
|
438
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
439
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
440
|
+
}
|
|
441
|
+
location /api/ {
|
|
442
|
+
proxy_pass ${APP_SCHEME}://${APP_HOST}:${APP_PORT}/api/;
|
|
443
|
+
proxy_http_version 1.1;
|
|
444
|
+
proxy_set_header Host $host;
|
|
445
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
446
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
447
|
+
proxy_set_header Connection "";
|
|
448
|
+
proxy_buffering off;
|
|
449
|
+
proxy_cache off;
|
|
450
|
+
proxy_read_timeout 3600s;
|
|
451
|
+
proxy_send_timeout 3600s;
|
|
452
|
+
proxy_connect_timeout 30s;
|
|
453
|
+
proxy_next_upstream error timeout http_502 http_503 http_504;
|
|
454
|
+
proxy_next_upstream_tries 3;
|
|
455
|
+
}
|
|
456
|
+
location / {
|
|
457
|
+
root /usr/share/nginx/html;
|
|
458
|
+
try_files $uri $uri/ /index.html;
|
|
459
|
+
add_header Cache-Control "no-cache, must-revalidate";
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
NGX
|
|
463
|
+
chmod 644 "$TPL"
|
|
464
|
+
fi
|
|
465
|
+
|
|
466
|
+
uninstall:
|
|
467
|
+
- deleteImage: "wechatopenai/weknora-docreader:latest"
|
|
468
|
+
- deleteImage: "paradedb/paradedb:v0.22.2-pg17"
|
|
469
|
+
- deleteImage: "wechatopenai/weknora-app:latest"
|
|
470
|
+
- deleteImage: "wechatopenai/weknora-ui:latest"
|
|
471
|
+
- deleteImage: "redis:7.0-alpine"
|
package/dist/cli/app.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "fs";
|
|
2
|
+
import { basename, extname } from "path";
|
|
2
3
|
import { stringify } from "yaml";
|
|
3
4
|
import { parseFlag } from "./helpers.js";
|
|
4
5
|
import { loadManagedListEntries, printManagedList } from "./managed-list.js";
|
|
@@ -328,6 +329,30 @@ function parseRemoteYamlUrl(value) {
|
|
|
328
329
|
throw e;
|
|
329
330
|
}
|
|
330
331
|
}
|
|
332
|
+
const BUILTIN_INSTALL_ALIASES = {
|
|
333
|
+
ollama: "ollama-binary.yaml",
|
|
334
|
+
};
|
|
335
|
+
async function loadBuiltinInstallYaml(source) {
|
|
336
|
+
const normalizedSource = source.trim();
|
|
337
|
+
if (!normalizedSource)
|
|
338
|
+
return null;
|
|
339
|
+
const { listBuiltinAppSpecs } = await import("../services/app/app-manager.js");
|
|
340
|
+
const templates = listBuiltinAppSpecs();
|
|
341
|
+
const normalizedWithoutExt = normalizedSource.replace(/\.ya?ml$/i, "");
|
|
342
|
+
const aliasTarget = BUILTIN_INSTALL_ALIASES[normalizedWithoutExt] ?? BUILTIN_INSTALL_ALIASES[normalizedSource];
|
|
343
|
+
const template = templates.find((entry) => {
|
|
344
|
+
if (aliasTarget && entry.fileName === aliasTarget)
|
|
345
|
+
return true;
|
|
346
|
+
if (entry.fileName === normalizedSource)
|
|
347
|
+
return true;
|
|
348
|
+
if (basename(entry.fileName, extname(entry.fileName)) === normalizedWithoutExt)
|
|
349
|
+
return true;
|
|
350
|
+
if (entry.id === normalizedWithoutExt || entry.id === normalizedSource)
|
|
351
|
+
return true;
|
|
352
|
+
return false;
|
|
353
|
+
});
|
|
354
|
+
return template?.yaml ?? null;
|
|
355
|
+
}
|
|
331
356
|
async function loadInstallYaml(source) {
|
|
332
357
|
const remoteUrl = parseRemoteYamlUrl(source);
|
|
333
358
|
if (remoteUrl) {
|
|
@@ -347,6 +372,10 @@ async function loadInstallYaml(source) {
|
|
|
347
372
|
}
|
|
348
373
|
return body;
|
|
349
374
|
}
|
|
375
|
+
const builtinYaml = await loadBuiltinInstallYaml(source);
|
|
376
|
+
if (builtinYaml) {
|
|
377
|
+
return builtinYaml;
|
|
378
|
+
}
|
|
350
379
|
if (!existsSync(source)) {
|
|
351
380
|
throw new Error(`File not found: ${source}`);
|
|
352
381
|
}
|
|
@@ -377,12 +406,41 @@ async function cmdShow(id) {
|
|
|
377
406
|
}
|
|
378
407
|
console.log(JSON.stringify(instance, null, 2));
|
|
379
408
|
}
|
|
380
|
-
async function cmdInstall(
|
|
381
|
-
const
|
|
409
|
+
async function cmdInstall(args) {
|
|
410
|
+
const installSource = args[0];
|
|
411
|
+
let requestedAppId;
|
|
412
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
413
|
+
const value = args[index];
|
|
414
|
+
if (value === "--sudo-password") {
|
|
415
|
+
index += 1;
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (value.startsWith("--"))
|
|
419
|
+
continue;
|
|
420
|
+
requestedAppId = value;
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
const yamlText = await loadInstallYaml(installSource);
|
|
382
424
|
const { installApp } = await import("../services/app/app-manager.js");
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
425
|
+
const sudoState = getCliSudoState(args);
|
|
426
|
+
while (true) {
|
|
427
|
+
try {
|
|
428
|
+
const installOptions = sudoState.password ? { exec: { sudoPassword: sudoState.password } } : undefined;
|
|
429
|
+
const result = installOptions
|
|
430
|
+
? await installApp(yamlText, requestedAppId, installOptions)
|
|
431
|
+
: await installApp(yamlText, requestedAppId);
|
|
432
|
+
const installModeLabel = result.manifest.install_mode === "instance-dir" ? "instance" : "app";
|
|
433
|
+
log(c.green(` ✓ Installed: ${result.manifest.id} (${result.spec.name || ""} v${result.spec.version || "?"}, ${installModeLabel})`));
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
catch (err) {
|
|
437
|
+
const message = err?.message || "Install failed";
|
|
438
|
+
if (isSudoPasswordError(message) && await promptForCliSudoPassword(sudoState)) {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
throw err;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
386
444
|
}
|
|
387
445
|
async function cmdProvides(args) {
|
|
388
446
|
const { listProvidedCapabilities } = await import("../services/app/app-manager.js");
|
|
@@ -504,16 +562,26 @@ async function cmdUninstallAll(args = []) {
|
|
|
504
562
|
}
|
|
505
563
|
}
|
|
506
564
|
async function cmdCreateInstance(appId, instanceId, name) {
|
|
507
|
-
const { getApp, installApp,
|
|
565
|
+
const { getApp, installApp, updateInstance } = await import("../services/app/app-manager.js");
|
|
566
|
+
const { resolveConnections, resolvedToLegacyEnv } = await import("../services/connection-resolver.js");
|
|
508
567
|
const appData = getApp(appId);
|
|
509
568
|
if (!appData) {
|
|
510
569
|
log(c.red(` ✗ App "${appId}" not found.`));
|
|
511
570
|
process.exitCode = 1;
|
|
512
571
|
return;
|
|
513
572
|
}
|
|
573
|
+
// PR 3 sub-step 3e: switch CLI to resolveConnections (preCreate). Pending
|
|
574
|
+
// requires are printed so the user can see what still needs binding.
|
|
514
575
|
let resolvedEnv = {};
|
|
515
576
|
try {
|
|
516
|
-
|
|
577
|
+
const { resolved, pending } = resolveConnections(appData.spec, { connections: {} }, "preCreate");
|
|
578
|
+
resolvedEnv = resolvedToLegacyEnv(resolved);
|
|
579
|
+
if (pending.length > 0) {
|
|
580
|
+
log(c.yellow(` ⚠ ${pending.length} pending connection(s):`));
|
|
581
|
+
for (const p of pending) {
|
|
582
|
+
log(c.yellow(` - ${p.slot} (${p.capability}): ${p.reason}`));
|
|
583
|
+
}
|
|
584
|
+
}
|
|
517
585
|
}
|
|
518
586
|
catch (e) {
|
|
519
587
|
log(c.red(` ✗ ${e.message}`));
|
|
@@ -705,7 +773,7 @@ export async function run(rest) {
|
|
|
705
773
|
await cmdShow(rest[1]);
|
|
706
774
|
}
|
|
707
775
|
else if (appCmd === "install" && rest[1]) {
|
|
708
|
-
await cmdInstall(rest
|
|
776
|
+
await cmdInstall(rest.slice(1));
|
|
709
777
|
}
|
|
710
778
|
else if (appCmd === "uninstall" && rest[1] === "--all") {
|
|
711
779
|
await cmdUninstallAll(rest.slice(2));
|
|
@@ -781,7 +849,8 @@ Commands:
|
|
|
781
849
|
list [--json] 列出统一受管列表(apps 下已安装应用 + instances 下遗留实例)
|
|
782
850
|
provides [--json] 列出已安装 App 声明/注册的能力
|
|
783
851
|
show <id> 查看 App 或实例详情(JSON)
|
|
784
|
-
install <yaml-file|https-url> [app-id]
|
|
852
|
+
install <yaml-file|https-url|builtin-id> [app-id] [--sudo-password <password>]
|
|
853
|
+
从本地、HTTPS 或内置模板安装 App,可选指定安装 id
|
|
785
854
|
uninstall <app-id> [--sudo-password <password>] 卸载单个 App 或实例(停止 + 删除目录)
|
|
786
855
|
uninstall --all [--sudo-password <password>] 卸载全部 App 与实例
|
|
787
856
|
create-instance <app-id> <inst-id> [name] 从 App 创建实例(Agent 通道由 spec.agentType 决定)
|
|
@@ -798,6 +867,7 @@ Examples:
|
|
|
798
867
|
jishushell app install ./apps/ollama-with-hollama-binary.yaml
|
|
799
868
|
jishushell app install ./apps/ollama-with-hollama-binary.yaml ollama-local
|
|
800
869
|
jishushell app install https://example.com/apps/ollama-with-hollama-binary.yaml
|
|
870
|
+
jishushell app install ollama
|
|
801
871
|
jishushell app list
|
|
802
872
|
jishushell app provides
|
|
803
873
|
jishushell app copy ollama-1
|