fix: allow target-side k3sctl deploy
This commit is contained in:
@@ -5,6 +5,7 @@ UniDesk 的统一 CLI 入口是根目录 `scripts/cli.ts`,运行方式固定
|
||||
## Command Model
|
||||
|
||||
- `help` 输出命令索引,适合作为交互式入口。
|
||||
- 每个 CLI 命名空间必须支持 `help`、`--help` 或 `-h` 并返回 JSON,不得为了打印帮助而访问 runtime 服务、拉起交互会话或执行长时任务。
|
||||
- `--main-server-ip <ip> <command>` 默认通过公网 frontend 登录态调用主 server 的同源 API 代理,不要求计算节点持有主 server SSH key;显式提供 `--main-server-key` 或 `--main-server-transport ssh` 时才使用旧 SSH 传输。
|
||||
- `config show` 读取并校验根目录 `config.json`,不从环境变量、默认值或隐藏文件静默补配置。
|
||||
- `check` 默认只执行轻量配置校验、Bun 版本检查和 Bun Transpiler 语法解析(覆盖 CLI 入口、主要 `scripts/` 模块和核心组件入口,不做类型推导);关键文件存在性、`scripts/` TypeScript 类型检查、`src/components/` TypeScript 类型检查、Docker Compose config 和日志轮转策略扫描默认不启用,分别通过 `--files`、`--scripts-typecheck`、`--components`、`--compose`、`--logs` 开启,或用 `--full` 一次性开启。`--rust` 只允许在 D601 CI/dev execution 中配合 `UNIDESK_D601_RUST_CHECK=1` 使用,长期规则见 `docs/reference/dev-environment.md`。
|
||||
|
||||
@@ -42,6 +42,8 @@ The optional non-service execution declaration under `environments.dev` is inten
|
||||
|
||||
Environment mode never reads the local dirty working tree manifest. `deploy check --env ...`, `deploy plan --env ...` and `deploy apply --env ...` fetch `origin/master`, read `origin/master:deploy.json`, select `environments.<env>`, and report the manifest commit/blob, service commit IDs, target namespace, database fingerprint and Provider identity. `deploy apply --env dev` is currently enabled only for persistent D601 dev `backend-core` and `frontend`; all other D601 services remain rejected before runtime mutation. `deploy apply --env prod` remains disabled until the production environment executor and authorization policy are explicitly added.
|
||||
|
||||
The only D601 direct-service exception in local manifest mode is `k3sctl-adapter`, because it is the UniDesk-managed control bridge outside the k3s fault domain and owns the Kubernetes service catalog used by the dev public frontend path. Updating it must still use the normal target-side deploy reconciler from a pushed commit. D601 Code Queue, Decision Center, MDTODO, ClaudeQQ and future k3s-managed workloads remain blocked from maintenance-channel direct deploy.
|
||||
|
||||
`config.json.microservices[].repository.commitId` is retained for catalog compatibility, but `deploy.json` is the deployment version authority for the reconciler.
|
||||
|
||||
## Dev CI Runner
|
||||
|
||||
+30
-113
@@ -10,128 +10,19 @@ import { extractRemoteCliOptions, runRemoteCli } from "./src/remote";
|
||||
import { runMicroserviceCommand } from "./src/microservices";
|
||||
import { runCodeQueueCommand } from "./src/code-queue";
|
||||
import { runDecisionCenterCommand } from "./src/decision-center";
|
||||
import { runCodeQueueDeployCompatCommand, runDeployCommand } from "./src/deploy";
|
||||
import { deployHelp, runCodeQueueDeployCompatCommand, runDeployCommand } from "./src/deploy";
|
||||
import { runProviderCommand } from "./src/provider-attach";
|
||||
import { runScheduleCommand } from "./src/schedules";
|
||||
import { parseNetworkPerfOptions, runNetworkPerf } from "./src/network-perf";
|
||||
import { runCiCommand } from "./src/ci";
|
||||
import { ciHelp, runCiCommand } from "./src/ci";
|
||||
import { runSwapCommand } from "./src/swap";
|
||||
import { runDevEnvCommand } from "./src/dev-env";
|
||||
import { isHelpToken, rootHelp, serverHelp, sshHelp, staticNamespaceHelp } from "./src/help";
|
||||
|
||||
const remoteOptions = extractRemoteCliOptions(process.argv.slice(2));
|
||||
const args = remoteOptions.args;
|
||||
const commandName = args.join(" ") || "help";
|
||||
|
||||
function help(): unknown {
|
||||
return {
|
||||
entry: "bun scripts/cli.ts",
|
||||
output: "json",
|
||||
commands: [
|
||||
{ command: "help", description: "List supported commands." },
|
||||
{ command: "--main-server-ip <ip> <command>", description: "Run selected commands through the public frontend API; use --main-server-key only for legacy SSH transport." },
|
||||
{ command: "config show", description: "Validate and print config.json as the single source of truth." },
|
||||
{ command: "check [--full|--files|--scripts-typecheck|--components|--compose|--logs|--rust]", description: "Run the lightweight default syntax/config gate; Rust is opt-in and only allowed from D601 CI/dev execution." },
|
||||
{ command: "server start", description: "Fire-and-forget build/start for database, backend-core, frontend, provider gateway, and managed main-server user services." },
|
||||
{ command: "server stop", description: "Fire-and-forget docker-compose down for the fixed UniDesk stack." },
|
||||
{ command: "server status", description: "Show fixed ports, containers, service health, and public URLs." },
|
||||
{ command: "server swap status|ensure [--path /swapfile] [--size 2GiB] [--dry-run]", description: "Inspect or idempotently create host swap for low-memory main-server operation." },
|
||||
{ command: "server logs [--tail-bytes N]", description: "Return bounded tails from file logs and docker logs." },
|
||||
{ command: "server rebuild <backend-core|frontend|dev-frontend-proxy|provider-gateway|todo-note|code-queue-mgr|project-manager|baidu-netdisk|oa-event-flow>", description: "Build first, then serialize, force-recreate, and validate one Compose service." },
|
||||
{ command: "provider attach <providerId> [--master-server URL] [--up] [--force]", description: "Generate the minimal external provider-gateway env/compose bundle; only master server URL and provider id are required." },
|
||||
{ command: "ssh <providerId> [ssh-like args...]", description: "Open a Host SSH / WSL SSH maintenance session through the provider-gateway bridge with built-in remote helper tools in PATH." },
|
||||
{ command: "ssh <providerId> apply-patch [tool args...] < patch.diff", description: "Invoke the injected remote apply_patch helper directly over SSH passthrough and stream the patch from local stdin." },
|
||||
{ command: "ssh <providerId> py [script-args...] < script.py", description: "Run remote Python from local stdin through SSH passthrough without nested shell quoting; extra args become script argv." },
|
||||
{ command: "ssh <providerId> skills [--scope all|wsl|windows] [--limit N]", description: "Discover WSL/Linux and, for WSL providers, Windows skill directories in one SSH passthrough call." },
|
||||
{ command: "ssh <providerId> find <path...> [--max-depth N] [--type d|f|l] [--contains TEXT] [--iname PATTERN] [--limit N] [--sort]", description: "Run a structured remote find command without nested shell quoting or parentheses." },
|
||||
{ command: "ssh <providerId> glob [--root DIR] [--pattern PATTERN] [--contains TEXT] [--type any|f|d] [--limit N] [--sort]", description: "Run remote glob matching through the injected helper without shell glob expansion." },
|
||||
{ command: "ssh <providerId> argv <command> [args...]", description: "Run a remote command with each argv token shell-quoted by UniDesk before SSH passthrough." },
|
||||
{ command: "microservice list", description: "List UniDesk-managed user services and their provider/runtime mapping." },
|
||||
{ command: "microservice status <id>", description: "Show one user service config, repository reference, backend mapping, and runtime status." },
|
||||
{ command: "microservice health <id>", description: "Probe one user service through backend-core -> provider-gateway HTTP proxy." },
|
||||
{ command: "microservice proxy <id> <path> [--method GET|POST|PUT|PATCH|DELETE] [--body-json JSON|--body-file path|--body-stdin] [--raw] [--max-body-bytes N]", description: "Access a private user-service backend path through the same frontend-only proxy used by WebUI; JSON request bodies are supported for controlled write/debug endpoints." },
|
||||
{ command: "microservice diagnostics <id>", description: "Split k3sctl-managed proxy health into provider-gateway, HTTP tunnel, adapter, Kubernetes API service proxy, and target Service checks." },
|
||||
{ command: "microservice tunnel-self-test <id>", description: "Trigger an expected provider HTTP tunnel failure and verify requestId/stage diagnostics are returned." },
|
||||
{ command: "decision upload <markdown-file> [--title text] [--type meeting|decision] [--level G0|G1|G2|G3|P0|P1|P2|P3|none] [--status active|blocked|parked|done] [--linked-goal-id id] [--evidence url]", description: "Upload a meeting note or decision record through backend-core -> decision-center user-service proxy." },
|
||||
{ command: "decision diary import <markdown-file> [--source-file path] [--tag tag] [--include-entries]", description: "Import a dated work log Markdown into PostgreSQL diary entries split as YYYY-MM/YYYY-MM-DD.md." },
|
||||
{ command: "decision diary list [--month YYYY-MM] [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N] [--include-body]", description: "List daily Markdown diary entries stored by Decision Center." },
|
||||
{ command: "decision diary months", description: "List available Decision Center diary months with day counts." },
|
||||
{ command: "decision diary show <YYYY-MM-DD|id>", description: "Show one daily diary Markdown entry." },
|
||||
{ command: "decision list [--type ...] [--status ...] [--level ...] [--linked-goal-id id] [--limit N]", description: "List Decision Center records through the user-service proxy." },
|
||||
{ command: "decision show <id>", description: "Show one Decision Center record." },
|
||||
{ command: "deploy check|plan|apply [--file deploy.json|--env dev|prod] [--service id] [--dry-run] [--force]", description: "Reconcile services from a repo+commit manifest; --env reads origin/master:deploy.json environments and can apply supported dev services." },
|
||||
{ command: "dev-env validate|prewarm-images", description: "Validate D601 unidesk-dev guardrails or prewarm dev foundation images into native k3s containerd through a bounded async job." },
|
||||
{ command: "schedule list|get|runs|run|delete", description: "Manage backend-core scheduled tasks and run history; schedule run <id> supports --wait-ms N." },
|
||||
{ command: "schedule upsert-pgdata-backup [--time HH:MM] [--remote-base /SERVER_DATA/UNIDESK_PG_DATA]", description: "Create or update the daily PGDATA physical backup task that uploads monthly rotated archives to Baidu Netdisk." },
|
||||
{ command: "codex deploy <commitId> [--provider-id D601] [--timeout-ms N]", description: "Compatibility wrapper for deploy apply --service code-queue with a temporary repo+commit manifest." },
|
||||
{ command: "codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue queueId] [--provider-id id] [--cwd path] [--model model] [--execution-mode mode] [--max-attempts N] [--reference-task-id id] [--dry-run]", description: "Submit a Code Queue task through backend-core -> code-queue proxy; --dry-run shows the structured request without enqueueing." },
|
||||
{ command: "codex task <taskId> [--trace --tail|--from-start|--after-seq N|--before-seq N --limit N] [--full]", description: "Fetch a compact Code Queue task summary; trace rows are opt-in and paged with next/previous commands to avoid output explosion." },
|
||||
{ command: "codex output <taskId> [--tail|--from-start|--after-seq N|--before-seq N --limit N] [--full-text]", description: "Fetch paged raw Code Queue output records by seq when a trace row has omitted command/output text." },
|
||||
{ command: "codex judge <taskId> --attempt N [--dry-run] [--include-prompt]", description: "Replay one stored Code Queue attempt through the same judge context builder and MiniMax judge call path used by the live queue worker." },
|
||||
{ command: "codex interrupt|cancel <taskId>", description: "Request interrupt for a running Code Queue task, or cancel a queued/retry_wait task, through the same private proxy." },
|
||||
{ command: "codex (queues | queue create <queueId> | queue merge <sourceQueueId> --into <targetQueueId> | move <taskId> --queue <queueId>)", description: "List/create/merge Code Queue lanes and move a queued task; merge preserves task queue time order and deletes the source queue record." },
|
||||
{ command: "job list [--limit N] [--include-command]", description: "List async jobs from .state/jobs with a bounded default page." },
|
||||
{ command: "job status <jobId|latest> [--tail-bytes N]", description: "Show job state with bounded stdout/stderr tails." },
|
||||
{ command: "debug health", description: "Probe internal core, nodes, system/Docker status, frontend, provider ingress, and public boundary." },
|
||||
{ command: "debug dispatch [providerId] [docker.ps|provider.upgrade|host.ssh|microservice.http|echo] [--wait-ms N]", description: "Submit a real internal-core dispatch request for CLI debugging." },
|
||||
{ command: "debug task <taskId|latest>", description: "Read a dispatched task record from internal core for CLI debugging." },
|
||||
{ command: "network perf [--service code-queue --path /api/tasks/overview?limit=30 --count N --concurrency N --label before|after]", description: "Benchmark frontend -> backend-core -> provider/adapter user-service networking and report latency/proxy-mode distributions." },
|
||||
{ command: "ci install|status|run|run-dev-e2e|logs", description: "Manage D601 k3s Tekton CI only; run-dev-e2e manually validates master deploy.json dev state in an isolated temporary namespace." },
|
||||
{ command: "e2e run [--only pattern[,pattern...]] [--skip pattern[,pattern...]]", description: "Run selected public/internal/Playwright E2E checks; use --only for focused iteration and rerun without filters for final regression." },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function isHelpToken(value: string | undefined): boolean {
|
||||
return value === "help" || value === "--help" || value === "-h";
|
||||
}
|
||||
|
||||
function serverHelp(action: string | undefined = undefined): unknown {
|
||||
return {
|
||||
command: action === undefined || isHelpToken(action) ? "server start|stop|status|swap|logs|rebuild" : `server ${action}`,
|
||||
output: "json",
|
||||
description: "Manage the fixed main-server Docker Compose stack without exposing backend-core REST publicly.",
|
||||
usage: {
|
||||
start: "bun scripts/cli.ts server start",
|
||||
stop: "bun scripts/cli.ts server stop",
|
||||
status: "bun scripts/cli.ts server status",
|
||||
swap: "bun scripts/cli.ts server swap status|ensure [--path /swapfile] [--size 2GiB] [--dry-run]",
|
||||
logs: "bun scripts/cli.ts server logs [--tail-bytes N]",
|
||||
rebuild: "bun scripts/cli.ts server rebuild <backend-core|frontend|dev-frontend-proxy|provider-gateway|todo-note|code-queue-mgr|project-manager|baidu-netdisk|oa-event-flow>",
|
||||
},
|
||||
publicEntrypoints: {
|
||||
frontend: "prod UniDesk frontend",
|
||||
devFrontend: "dev UniDesk frontend proxy to D601 unidesk-dev/frontend-dev",
|
||||
providerIngress: "provider-gateway WebSocket ingress",
|
||||
},
|
||||
rustBoundary: {
|
||||
masterServer: "do not use server rebuild backend-core for Rust iteration; it would build locally",
|
||||
d601: "use deploy apply --env dev --service backend-core and CI for Rust build/check",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function sshHelp(): unknown {
|
||||
return {
|
||||
command: "ssh",
|
||||
output: "json",
|
||||
description: "Open a Host SSH / WSL SSH maintenance session through the provider-gateway bridge.",
|
||||
usage: [
|
||||
"bun scripts/cli.ts ssh <providerId>",
|
||||
"bun scripts/cli.ts ssh <providerId> argv <command> [args...]",
|
||||
"bun scripts/cli.ts ssh <providerId> apply-patch < patch.diff",
|
||||
"bun scripts/cli.ts ssh <providerId> py [script-args...] < script.py",
|
||||
"bun scripts/cli.ts ssh <providerId> skills [--scope all|wsl|windows] [--limit N]",
|
||||
"bun scripts/cli.ts ssh <providerId> find <path...> [--contains TEXT] [--limit N]",
|
||||
"bun scripts/cli.ts ssh <providerId> glob [--root DIR] [--pattern PATTERN]",
|
||||
],
|
||||
notes: [
|
||||
"ssh --help and ssh <providerId> --help print this JSON help and never open an interactive session.",
|
||||
"Use argv when nested shell quoting would be fragile.",
|
||||
"Use -- before a remote command that intentionally starts with a dash.",
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function numberOption(name: string, defaultValue: number): number {
|
||||
const index = args.indexOf(name);
|
||||
if (index === -1) return defaultValue;
|
||||
@@ -194,7 +85,7 @@ async function main(): Promise<void> {
|
||||
|
||||
const [top, sub, third, fourth] = args;
|
||||
if (top === undefined || top === "help" || top === "--help" || top === "-h") {
|
||||
emitJson(commandName, help());
|
||||
emitJson(commandName, rootHelp());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -203,6 +94,32 @@ async function main(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (top === "check" && isHelpToken(sub)) {
|
||||
emitJson(commandName, checkHelp());
|
||||
return;
|
||||
}
|
||||
|
||||
if (top === "server" && (isHelpToken(sub) || args.slice(2).some(isHelpToken))) {
|
||||
emitJson(commandName, serverHelp(isHelpToken(sub) ? undefined : sub));
|
||||
return;
|
||||
}
|
||||
|
||||
if (top === "deploy" && args.slice(1).some(isHelpToken)) {
|
||||
emitJson(commandName, deployHelp(isHelpToken(sub) ? undefined : sub));
|
||||
return;
|
||||
}
|
||||
|
||||
if (top === "ci" && (isHelpToken(sub) || args.slice(1).some(isHelpToken))) {
|
||||
emitJson(commandName, ciHelp());
|
||||
return;
|
||||
}
|
||||
|
||||
const namespaceHelp = staticNamespaceHelp(args);
|
||||
if (namespaceHelp !== null) {
|
||||
emitJson(commandName, namespaceHelp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (top === "internal" && sub === "run-job") {
|
||||
if (!third) throw new Error("internal run-job requires job id");
|
||||
emitJson(commandName, await runJob(third));
|
||||
|
||||
@@ -20,6 +20,7 @@ const syntaxFiles = [
|
||||
"scripts/src/deploy.ts",
|
||||
"scripts/src/docker.ts",
|
||||
"scripts/src/e2e.ts",
|
||||
"scripts/src/help.ts",
|
||||
"scripts/src/remote.ts",
|
||||
"src/components/frontend/src/index.ts",
|
||||
"src/components/frontend/src/app.tsx",
|
||||
|
||||
+2
-2
@@ -779,7 +779,7 @@ async function logs(name: string): Promise<Record<string, unknown>> {
|
||||
};
|
||||
}
|
||||
|
||||
function help(): Record<string, unknown> {
|
||||
export function ciHelp(): Record<string, unknown> {
|
||||
return {
|
||||
command: "ci install|status|run|run-dev-e2e|logs",
|
||||
description: "Manage the D601 k3s Tekton CI gate. This intentionally does not deploy CD.",
|
||||
@@ -815,7 +815,7 @@ function requireRunId(value: string): string {
|
||||
|
||||
export async function runCiCommand(_config: UniDeskConfig, args: string[]): Promise<Record<string, unknown>> {
|
||||
const [action = "status", nameArg] = args;
|
||||
if (isHelpArg(action) || args.slice(1).some(isHelpArg)) return help();
|
||||
if (isHelpArg(action) || args.slice(1).some(isHelpArg)) return ciHelp();
|
||||
if (action === "install") return install();
|
||||
if (action === "status") return status();
|
||||
if (action === "run") {
|
||||
|
||||
@@ -131,7 +131,7 @@ const nativeK3sInstallVersion = "v1.34.1+k3s1";
|
||||
const nativeK3sImage = "rancher/k3s:v1.34.1-k3s1";
|
||||
const nativeK3sCtrAddress = "/run/k3s/containerd/containerd.sock";
|
||||
const unideskRepoUrl = "https://github.com/pikasTech/unidesk";
|
||||
const d601MaintenanceDeployAllowedServiceIds = new Set<string>(["backend-core", "frontend"]);
|
||||
const d601MaintenanceDeployAllowedServiceIds = new Set<string>(["backend-core", "frontend", "k3sctl-adapter"]);
|
||||
const devApplySupportedServiceIds = new Set<string>(["backend-core", "frontend"]);
|
||||
const deployEnvironmentTargets: Record<DeployEnvironment, DeployEnvironmentTarget> = {
|
||||
dev: {
|
||||
@@ -178,7 +178,7 @@ function isHelpArg(value: string | undefined): boolean {
|
||||
return value === "help" || value === "--help" || value === "-h";
|
||||
}
|
||||
|
||||
function deployHelp(action: string | undefined = undefined): Record<string, unknown> {
|
||||
export function deployHelp(action: string | undefined = undefined): Record<string, unknown> {
|
||||
const command = action === undefined || isHelpArg(action) ? "deploy check|plan|apply" : `deploy ${action}`;
|
||||
return {
|
||||
ok: true,
|
||||
@@ -194,8 +194,8 @@ function deployHelp(action: string | undefined = undefined): Record<string, unkn
|
||||
apply: "Start an async target-side reconcile job unless --run-now is explicitly present.",
|
||||
},
|
||||
options: [
|
||||
{ name: "--file <path>", default: defaultDeployFile, description: "Desired-state manifest path relative to the repo root. JSON and ESM JS manifests are supported, for example deploy.json or develop.js." },
|
||||
{ name: "--env <dev|prod>", description: "Read the named environment from origin/master:deploy.json. Dev apply is enabled only for backend-core and frontend in D601 unidesk-dev." },
|
||||
{ name: "--file <path>", default: defaultDeployFile, description: "Desired-state manifest path relative to the repo root. JSON and ESM JS manifests are supported, for example deploy.json or develop.js. Local manifest apply has one D601 direct-service exception: k3sctl-adapter." },
|
||||
{ name: "--env <dev|prod>", description: "Read the named environment from origin/master:deploy.json. Dev apply is enabled only for backend-core and frontend in D601 unidesk-dev; prod apply is disabled." },
|
||||
{ name: "--service <id>", description: "Limit reconcile to one service from the manifest." },
|
||||
{ name: "--dry-run", description: "Prepare and validate without mutating the target service." },
|
||||
{ name: "--force", description: "Redeploy even when the live commit appears up to date." },
|
||||
@@ -2170,7 +2170,7 @@ async function applyOneService(config: UniDeskConfig, service: UniDeskMicroservi
|
||||
ok: false,
|
||||
serviceId: service.id,
|
||||
skipped: true,
|
||||
reason: `D601 dev deployment is allowed only for backend-core and frontend through the controlled deploy --env dev path; ${service.id} is not enabled. Use ci run-dev-e2e for smoke verification.`,
|
||||
reason: `D601 target-side deployment is allowed only for k3sctl-adapter and dev backend-core/frontend; ${service.id} is not enabled. Use ci run-dev-e2e for smoke verification.`,
|
||||
steps,
|
||||
};
|
||||
}
|
||||
@@ -2345,7 +2345,7 @@ function blockedD601MaintenanceDeployServices(config: UniDeskConfig, manifest: D
|
||||
}
|
||||
|
||||
function d601MaintenanceDeployBlockMessage(blocked: string[]): string {
|
||||
return `D601 dev deployment is enabled only for backend-core and frontend through deploy --env dev; blocked services: ${blocked.join(", ")}. Use ci run-dev-e2e for dev smoke verification.`;
|
||||
return `D601 target-side deployment is enabled only for k3sctl-adapter and dev backend-core/frontend; blocked services: ${blocked.join(", ")}. Use ci run-dev-e2e for dev smoke verification.`;
|
||||
}
|
||||
|
||||
async function runApplyNow(config: UniDeskConfig, manifest: DeployManifest, options: DeployOptions): Promise<Record<string, unknown>> {
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
export function rootHelp(): unknown {
|
||||
return {
|
||||
entry: "bun scripts/cli.ts",
|
||||
output: "json",
|
||||
commands: [
|
||||
{ command: "help", description: "List supported commands." },
|
||||
{ command: "--main-server-ip <ip> <command>", description: "Run selected commands through the public frontend API; use --main-server-key only for legacy SSH transport." },
|
||||
{ command: "config show", description: "Validate and print config.json as the single source of truth." },
|
||||
{ command: "check [--full|--files|--scripts-typecheck|--components|--compose|--logs|--rust]", description: "Run the lightweight default syntax/config gate; Rust is opt-in and only allowed from D601 CI/dev execution." },
|
||||
{ command: "server start", description: "Fire-and-forget build/start for database, backend-core, frontend, provider gateway, and managed main-server user services." },
|
||||
{ command: "server stop", description: "Fire-and-forget docker-compose down for the fixed UniDesk stack." },
|
||||
{ command: "server status", description: "Show fixed ports, containers, service health, and public URLs." },
|
||||
{ command: "server swap status|ensure [--path /swapfile] [--size 2GiB] [--dry-run]", description: "Inspect or idempotently create host swap for low-memory main-server operation." },
|
||||
{ command: "server logs [--tail-bytes N]", description: "Return bounded tails from file logs and docker logs." },
|
||||
{ command: "server rebuild <backend-core|frontend|dev-frontend-proxy|provider-gateway|todo-note|code-queue-mgr|project-manager|baidu-netdisk|oa-event-flow>", description: "Build first, then serialize, force-recreate, and validate one Compose service." },
|
||||
{ command: "provider attach <providerId> [--master-server URL] [--up] [--force]", description: "Generate the minimal external provider-gateway env/compose bundle; only master server URL and provider id are required." },
|
||||
{ command: "ssh <providerId> [ssh-like args...]", description: "Open a Host SSH / WSL SSH maintenance session through the provider-gateway bridge with built-in remote helper tools in PATH." },
|
||||
{ command: "ssh <providerId> apply-patch [tool args...] < patch.diff", description: "Invoke the injected remote apply_patch helper directly over SSH passthrough and stream the patch from local stdin." },
|
||||
{ command: "ssh <providerId> py [script-args...] < script.py", description: "Run remote Python from local stdin through SSH passthrough without nested shell quoting; extra args become script argv." },
|
||||
{ command: "ssh <providerId> skills [--scope all|wsl|windows] [--limit N]", description: "Discover WSL/Linux and, for WSL providers, Windows skill directories in one SSH passthrough call." },
|
||||
{ command: "ssh <providerId> find <path...> [--max-depth N] [--type d|f|l] [--contains TEXT] [--iname PATTERN] [--limit N] [--sort]", description: "Run a structured remote find command without nested shell quoting or parentheses." },
|
||||
{ command: "ssh <providerId> glob [--root DIR] [--pattern PATTERN] [--contains TEXT] [--type any|f|d] [--limit N] [--sort]", description: "Run remote glob matching through the injected helper without shell glob expansion." },
|
||||
{ command: "ssh <providerId> argv <command> [args...]", description: "Run a remote command with each argv token shell-quoted by UniDesk before SSH passthrough." },
|
||||
{ command: "microservice list", description: "List UniDesk-managed user services and their provider/runtime mapping." },
|
||||
{ command: "microservice status <id>", description: "Show one user service config, repository reference, backend mapping, and runtime status." },
|
||||
{ command: "microservice health <id>", description: "Probe one user service through backend-core -> provider-gateway HTTP proxy." },
|
||||
{ command: "microservice proxy <id> <path> [--method GET|POST|PUT|PATCH|DELETE] [--body-json JSON|--body-file path|--body-stdin] [--raw] [--max-body-bytes N]", description: "Access a private user-service backend path through the same frontend-only proxy used by WebUI; JSON request bodies are supported for controlled write/debug endpoints." },
|
||||
{ command: "microservice diagnostics <id>", description: "Split k3sctl-managed proxy health into provider-gateway, HTTP tunnel, adapter, Kubernetes API service proxy, and target Service checks." },
|
||||
{ command: "microservice tunnel-self-test <id>", description: "Trigger an expected provider HTTP tunnel failure and verify requestId/stage diagnostics are returned." },
|
||||
{ command: "decision upload <markdown-file> [--title text] [--type meeting|decision] [--level G0|G1|G2|G3|P0|P1|P2|P3|none] [--status active|blocked|parked|done] [--linked-goal-id id] [--evidence url]", description: "Upload a meeting note or decision record through backend-core -> decision-center user-service proxy." },
|
||||
{ command: "decision diary import <markdown-file> [--source-file path] [--tag tag] [--include-entries]", description: "Import a dated work log Markdown into PostgreSQL diary entries split as YYYY-MM/YYYY-MM-DD.md." },
|
||||
{ command: "decision diary list [--month YYYY-MM] [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N] [--include-body]", description: "List daily Markdown diary entries stored by Decision Center." },
|
||||
{ command: "decision diary months", description: "List available Decision Center diary months with day counts." },
|
||||
{ command: "decision diary show <YYYY-MM-DD|id>", description: "Show one daily diary Markdown entry." },
|
||||
{ command: "decision list [--type ...] [--status ...] [--level ...] [--linked-goal-id id] [--limit N]", description: "List Decision Center records through the user-service proxy." },
|
||||
{ command: "decision show <id>", description: "Show one Decision Center record." },
|
||||
{ command: "deploy check|plan|apply [--file deploy.json|--env dev|prod] [--service id] [--dry-run] [--force]", description: "Reconcile services from a repo+commit manifest; --env reads origin/master:deploy.json environments and can apply supported dev services." },
|
||||
{ command: "dev-env validate|prewarm-images", description: "Validate D601 unidesk-dev guardrails or prewarm dev foundation images into native k3s containerd through a bounded async job." },
|
||||
{ command: "schedule list|get|runs|run|delete", description: "Manage backend-core scheduled tasks and run history; schedule run <id> supports --wait-ms N." },
|
||||
{ command: "schedule upsert-pgdata-backup [--time HH:MM] [--remote-base /SERVER_DATA/UNIDESK_PG_DATA]", description: "Create or update the daily PGDATA physical backup task that uploads monthly rotated archives to Baidu Netdisk." },
|
||||
{ command: "codex deploy <commitId> [--provider-id D601] [--timeout-ms N]", description: "Compatibility wrapper for deploy apply --service code-queue with a temporary repo+commit manifest." },
|
||||
{ command: "codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue queueId] [--provider-id id] [--cwd path] [--model model] [--execution-mode mode] [--max-attempts N] [--reference-task-id id] [--dry-run]", description: "Submit a Code Queue task through backend-core -> code-queue proxy; --dry-run shows the structured request without enqueueing." },
|
||||
{ command: "codex task <taskId> [--trace --tail|--from-start|--after-seq N|--before-seq N --limit N] [--full]", description: "Fetch a compact Code Queue task summary; trace rows are opt-in and paged with next/previous commands to avoid output explosion." },
|
||||
{ command: "codex output <taskId> [--tail|--from-start|--after-seq N|--before-seq N --limit N] [--full-text]", description: "Fetch paged raw Code Queue output records by seq when a trace row has omitted command/output text." },
|
||||
{ command: "codex judge <taskId> --attempt N [--dry-run] [--include-prompt]", description: "Replay one stored Code Queue attempt through the same judge context builder and MiniMax judge call path used by the live queue worker." },
|
||||
{ command: "codex interrupt|cancel <taskId>", description: "Request interrupt for a running Code Queue task, or cancel a queued/retry_wait task, through the same private proxy." },
|
||||
{ command: "codex (queues | queue create <queueId> | queue merge <sourceQueueId> --into <targetQueueId> | move <taskId> --queue <queueId>)", description: "List/create/merge Code Queue lanes and move a queued task; merge preserves task queue time order and deletes the source queue record." },
|
||||
{ command: "job list [--limit N] [--include-command]", description: "List async jobs from .state/jobs with a bounded default page." },
|
||||
{ command: "job status <jobId|latest> [--tail-bytes N]", description: "Show job state with bounded stdout/stderr tails." },
|
||||
{ command: "debug health", description: "Probe internal core, nodes, system/Docker status, frontend, provider ingress, and public boundary." },
|
||||
{ command: "debug dispatch [providerId] [docker.ps|provider.upgrade|host.ssh|microservice.http|echo] [--wait-ms N]", description: "Submit a real internal-core dispatch request for CLI debugging." },
|
||||
{ command: "debug task <taskId|latest>", description: "Read a dispatched task record from internal core for CLI debugging." },
|
||||
{ command: "network perf [--service code-queue --path /api/tasks/overview?limit=30 --count N --concurrency N --label before|after]", description: "Benchmark frontend -> backend-core -> provider/adapter user-service networking and report latency/proxy-mode distributions." },
|
||||
{ command: "ci install|status|run|run-dev-e2e|logs", description: "Manage D601 k3s Tekton CI only; run-dev-e2e manually validates master deploy.json dev state in an isolated temporary namespace." },
|
||||
{ command: "e2e run [--only pattern[,pattern...]] [--skip pattern[,pattern...]]", description: "Run selected public/internal/Playwright E2E checks; use --only for focused iteration and rerun without filters for final regression." },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function isHelpToken(value: string | undefined): boolean {
|
||||
return value === "help" || value === "--help" || value === "-h";
|
||||
}
|
||||
|
||||
export function serverHelp(action: string | undefined = undefined): unknown {
|
||||
return {
|
||||
command: action === undefined || isHelpToken(action) ? "server start|stop|status|swap|logs|rebuild" : `server ${action}`,
|
||||
output: "json",
|
||||
description: "Manage the fixed main-server Docker Compose stack without exposing backend-core REST publicly.",
|
||||
usage: {
|
||||
start: "bun scripts/cli.ts server start",
|
||||
stop: "bun scripts/cli.ts server stop",
|
||||
status: "bun scripts/cli.ts server status",
|
||||
swap: "bun scripts/cli.ts server swap status|ensure [--path /swapfile] [--size 2GiB] [--dry-run]",
|
||||
logs: "bun scripts/cli.ts server logs [--tail-bytes N]",
|
||||
rebuild: "bun scripts/cli.ts server rebuild <backend-core|frontend|dev-frontend-proxy|provider-gateway|todo-note|code-queue-mgr|project-manager|baidu-netdisk|oa-event-flow>",
|
||||
},
|
||||
publicEntrypoints: {
|
||||
frontend: "prod UniDesk frontend",
|
||||
devFrontend: "dev UniDesk frontend proxy to D601 unidesk-dev/frontend-dev",
|
||||
providerIngress: "provider-gateway WebSocket ingress",
|
||||
},
|
||||
rustBoundary: {
|
||||
masterServer: "do not use server rebuild backend-core for Rust iteration; it would build locally",
|
||||
d601: "use deploy apply --env dev --service backend-core and CI for Rust build/check",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function sshHelp(): unknown {
|
||||
return {
|
||||
command: "ssh",
|
||||
output: "json",
|
||||
description: "Open a Host SSH / WSL SSH maintenance session through the provider-gateway bridge.",
|
||||
usage: [
|
||||
"bun scripts/cli.ts ssh <providerId>",
|
||||
"bun scripts/cli.ts ssh <providerId> argv <command> [args...]",
|
||||
"bun scripts/cli.ts ssh <providerId> apply-patch < patch.diff",
|
||||
"bun scripts/cli.ts ssh <providerId> py [script-args...] < script.py",
|
||||
"bun scripts/cli.ts ssh <providerId> skills [--scope all|wsl|windows] [--limit N]",
|
||||
"bun scripts/cli.ts ssh <providerId> find <path...> [--contains TEXT] [--limit N]",
|
||||
"bun scripts/cli.ts ssh <providerId> glob [--root DIR] [--pattern PATTERN]",
|
||||
],
|
||||
notes: [
|
||||
"ssh --help and ssh <providerId> --help print this JSON help and never open an interactive session.",
|
||||
"Use argv when nested shell quoting would be fragile.",
|
||||
"Use -- before a remote command that intentionally starts with a dash.",
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function configHelp(): unknown {
|
||||
return {
|
||||
command: "config show",
|
||||
output: "json",
|
||||
usage: "bun scripts/cli.ts config show",
|
||||
description: "Validate and print the root config.json single source of truth.",
|
||||
};
|
||||
}
|
||||
|
||||
function microserviceHelp(): unknown {
|
||||
return {
|
||||
command: "microservice list|status|health|diagnostics|tunnel-self-test|proxy",
|
||||
output: "json",
|
||||
usage: [
|
||||
"bun scripts/cli.ts microservice list",
|
||||
"bun scripts/cli.ts microservice status <id>",
|
||||
"bun scripts/cli.ts microservice health <id>",
|
||||
"bun scripts/cli.ts microservice diagnostics <id>",
|
||||
"bun scripts/cli.ts microservice tunnel-self-test <id>",
|
||||
"bun scripts/cli.ts microservice proxy <id> <path> [--method GET|POST|PUT|PATCH|DELETE] [--body-json JSON|--body-file path|--body-stdin] [--raw] [--full] [--max-body-bytes N]",
|
||||
],
|
||||
description: "Access UniDesk-managed user services through the same backend-core/provider proxy path used by the WebUI.",
|
||||
};
|
||||
}
|
||||
|
||||
function decisionHelp(): unknown {
|
||||
return {
|
||||
command: "decision upload|list|show|health|diary",
|
||||
output: "json",
|
||||
usage: [
|
||||
"bun scripts/cli.ts decision upload <markdown-file> [--title text] [--type meeting|decision]",
|
||||
"bun scripts/cli.ts decision list [--type ...] [--status ...] [--level ...] [--limit N]",
|
||||
"bun scripts/cli.ts decision show <id>",
|
||||
"bun scripts/cli.ts decision health",
|
||||
"bun scripts/cli.ts decision diary import|list|months|show ...",
|
||||
],
|
||||
description: "Operate Decision Center through the registered user-service proxy.",
|
||||
};
|
||||
}
|
||||
|
||||
function providerHelp(): unknown {
|
||||
return {
|
||||
command: "provider attach",
|
||||
output: "json",
|
||||
usage: "bun scripts/cli.ts provider attach <providerId> [--master-server URL] [--up] [--force]",
|
||||
description: "Generate the minimal provider-gateway attach env/compose bundle for a new compute node.",
|
||||
};
|
||||
}
|
||||
|
||||
function scheduleHelp(): unknown {
|
||||
return {
|
||||
command: "schedule list|get|runs|run|delete|upsert-pgdata-backup",
|
||||
output: "json",
|
||||
usage: [
|
||||
"bun scripts/cli.ts schedule list",
|
||||
"bun scripts/cli.ts schedule get <id>",
|
||||
"bun scripts/cli.ts schedule runs [id] [--limit N]",
|
||||
"bun scripts/cli.ts schedule run <id> [--wait-ms N]",
|
||||
"bun scripts/cli.ts schedule delete <id>",
|
||||
"bun scripts/cli.ts schedule upsert-pgdata-backup [--time HH:MM] [--remote-base path]",
|
||||
],
|
||||
description: "Manage backend-core scheduled tasks and run history through the private core API.",
|
||||
};
|
||||
}
|
||||
|
||||
function codexHelp(): unknown {
|
||||
return {
|
||||
command: "codex deploy|submit|task|output|judge|interrupt|cancel|queues|queue|move",
|
||||
output: "json",
|
||||
usage: [
|
||||
"bun scripts/cli.ts codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue id] [--dry-run]",
|
||||
"bun scripts/cli.ts codex task <taskId> [--trace --tail|--from-start|--after-seq N|--before-seq N --limit N] [--full]",
|
||||
"bun scripts/cli.ts codex output <taskId> [--tail|--from-start|--after-seq N|--before-seq N --limit N] [--full-text]",
|
||||
"bun scripts/cli.ts codex judge <taskId> --attempt N [--dry-run] [--include-prompt]",
|
||||
"bun scripts/cli.ts codex interrupt|cancel <taskId>",
|
||||
"bun scripts/cli.ts codex queues | queue create <queueId> | queue merge <sourceQueueId> --into <targetQueueId> | move <taskId> --queue <queueId>",
|
||||
],
|
||||
description: "Operate Code Queue through the stable backend-core private proxy path.",
|
||||
};
|
||||
}
|
||||
|
||||
function jobHelp(): unknown {
|
||||
return {
|
||||
command: "job list|status",
|
||||
output: "json",
|
||||
usage: [
|
||||
"bun scripts/cli.ts job list [--limit N] [--include-command]",
|
||||
"bun scripts/cli.ts job status <jobId|latest> [--tail-bytes N]",
|
||||
],
|
||||
description: "Inspect fire-and-forget job state from .state/jobs without streaming unbounded logs.",
|
||||
};
|
||||
}
|
||||
|
||||
function debugHelp(): unknown {
|
||||
return {
|
||||
command: "debug health|dispatch|task",
|
||||
output: "json",
|
||||
usage: [
|
||||
"bun scripts/cli.ts debug health",
|
||||
"bun scripts/cli.ts debug dispatch [providerId] [docker.ps|provider.upgrade|host.ssh|microservice.http|echo] [--wait-ms N]",
|
||||
"bun scripts/cli.ts debug task <taskId|latest>",
|
||||
],
|
||||
description: "Debug the real core/provider/dispatch paths; do not use these as formal TEST.md acceptance steps.",
|
||||
};
|
||||
}
|
||||
|
||||
function networkHelp(): unknown {
|
||||
return {
|
||||
command: "network perf",
|
||||
output: "json",
|
||||
usage: "bun scripts/cli.ts network perf [--service id --path /api/path --count N --concurrency N --label text]",
|
||||
description: "Benchmark frontend/backend/provider user-service networking with bounded JSON latency summaries.",
|
||||
};
|
||||
}
|
||||
|
||||
function e2eHelp(): unknown {
|
||||
return {
|
||||
command: "e2e run",
|
||||
output: "json",
|
||||
usage: "bun scripts/cli.ts e2e run [--only pattern[,pattern...]] [--skip pattern[,pattern...]]",
|
||||
description: "Run selected public/internal/Playwright E2E checks; use filters for focused iteration and full run for final regression.",
|
||||
};
|
||||
}
|
||||
|
||||
function devEnvHelp(): unknown {
|
||||
return {
|
||||
command: "dev-env validate|prewarm-images",
|
||||
output: "json",
|
||||
usage: [
|
||||
"bun scripts/cli.ts dev-env validate [--manifest path] [--kubectl-dry-run]",
|
||||
"bun scripts/cli.ts dev-env prewarm-images [--image image] [--provider-id D601] [--no-pull] [--dry-run]",
|
||||
],
|
||||
description: "Validate D601 unidesk-dev guardrails or prewarm foundation images into native k3s containerd.",
|
||||
};
|
||||
}
|
||||
|
||||
export function staticNamespaceHelp(args: string[]): unknown | null {
|
||||
const [top] = args;
|
||||
if (!args.slice(1).some(isHelpToken)) return null;
|
||||
if (top === "config") return configHelp();
|
||||
if (top === "microservice") return microserviceHelp();
|
||||
if (top === "decision" || top === "decision-center") return decisionHelp();
|
||||
if (top === "provider") return providerHelp();
|
||||
if (top === "schedule") return scheduleHelp();
|
||||
if (top === "codex") return codexHelp();
|
||||
if (top === "job") return jobHelp();
|
||||
if (top === "debug") return debugHelp();
|
||||
if (top === "network") return networkHelp();
|
||||
if (top === "e2e") return e2eHelp();
|
||||
if (top === "dev-env") return devEnvHelp();
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user