# SuperSDK 接入说明(SuperCLI) > 本文是 SuperCLI 的当前契约。不确定参数时运行 `supercli --help` 或 > `supercli --help`,不要从旧脚本猜测。 - 工具:`supercli`(npm 包 `@supersdk/supercli`) - Node.js:>= 18 - 默认项目配置:工作空间根的 `super.json` - 核心规则:一份配置只对应一个 SPID;SPID 只能从选中配置读取 ## 1. 最短接入路径 ```bash npm install -g @supersdk/supercli supercli login --environment prod # 交互式 init 会一次性询问控制台签发的 SPID supercli init my-workspace --name "Acme Suite" cd my-workspace supercli add android apps/android \ --project android-app --package com.acme.app --name "Acme App" supercli add site apps/web \ --project web --env-target prod --slug acme --env prod supercli build-all --output-dir .super/build supercli deploy --project web --env-target prod --build-dir .super/build ``` 已经有工程时用 `register`,它只登记配置,不拷模板、不改工程文件: ```bash supercli register android legacy/android \ --project android-app --package com.acme.app --name "Acme App" supercli register site legacy/web \ --project web --env-target prod --slug acme --env prod supercli register service services/api \ --project api --env-target prod --slug api --env prod ``` ## 2. 配置、模块和环境 `super.json` 使用 schemaVersion 5,保存项目基本信息、构建意图和命名部署目标。它不保存 token、secret 值、 本机环境变量值、构建状态或部署结果。 | 概念 | 选择方式 | 职责 | |---|---|---| | 平台项目身份 | `super.json` 的 `spid` | 一份配置一个 SPID;CLI 不接受 `--spid` | | 仓库内模块 | `projects[].projectId` / `--project` | 稳定选择 Android、iOS、site 或 service | | 命名部署目标 | `deploy[].envTarget` / `--env-target` | 选择配置中的 slug、平台 env 与运行规格 | | SDK 运行环境 | App 运行时 API | 决定 SDK 连 prod 还是 staging,与构建目标无关 | | CLI 登录环境 | `login --environment prod|staging` | 决定账号所在的 Console | `projectId` 不是 SPID,也不是平台 slug。`envTarget` 是配置内的目标名;target 里的 `env` 才是 Console 平台环境。analytics/db/logs/settings 现有的 `--env` 是业务查询维度, 不能替换为 `--env-target`。 ## 3. `super.json` ```json { "schemaVersion": 5, "spid": "a1b2c3d4", "orgId": "42", "name": "Acme Suite", "version": { "name": "1.0.0", "code": 1 }, "projects": [ { "projectId": "android-app", "type": "android", "dir": "apps/android", "name": "Acme App", "package": "com.acme.app", "build": { "entry": "scripts/build.sh", "args": ["--spid=${spid}"], "artifacts": [ { "id": "android-apk", "kind": "file", "path": ".supersdk/artifacts/app-debug.apk" } ] } }, { "projectId": "web", "type": "site", "dir": "apps/web", "build": { "entry": "scripts/build.sh", "args": ["--spid=${spid}"], "artifacts": [ { "id": "site-dist", "kind": "directory", "path": "dist" } ] }, "deploy": [ { "envTarget": "prod", "slug": "acme", "env": "prod", "name": "Acme Web" } ] }, { "projectId": "api", "type": "service", "dir": "services/api", "build": { "entry": "scripts/build.sh", "args": ["--spid=${spid}"], "artifacts": [ { "id": "service-image", "kind": "docker-image-archive", "path": ".supersdk/artifacts/image.tar" } ] }, "deploy": [ { "envTarget": "prod", "slug": "api", "env": "prod", "port": 8080 } ] } ] } ``` 约束: - `schemaVersion` 必须为 5,`spid` 是 8 位小写字母/数字。 - `projectId` 在配置内唯一,匹配 `^[a-z][a-z0-9-]{0,63}$`。 - 项目目录必须是 workspace 内的 POSIX 相对路径,彼此不得重合或嵌套。 - `envTarget` 在同一项目内唯一;`(slug, env)` 在整份配置内唯一。 - JSON 不允许重复键、注释、`NaN`/`Infinity` 或未知字段。 - 项目级 `version` 是完整覆盖;SDK/toolchain 版本归各项目的 Gradle、Swift、npm 或 Docker 文件管理。 - `build` 只能是 `{}`,或同时包含且只包含 `entry`、`args`、`artifacts` 的完整对象。 - `args` 保持 argv 数组边界,只允许 `${spid}` 变量;CLI 不执行 shell,也不展开环境变量。 - artifact path 相对项目目录;site 必须声明 `site-dist/directory`,service 必须声明 `service-image/docker-image-archive`。 - ENV、密钥、CPU/内存、资源请求、副本/扩缩容和数据库绑定不放在 `super.json`。 通过 Console 或 `super-run update`、`super-run env-vars set`、`db bind/unbind` 显式修改。 - 迁移兼容:旧 v5 的 `runtimeEnvFromEnv`/`secretsFromEnv`、资源及 `databaseConnection` 字段 暂时可读,但 deploy 全部忽略并输出 `RUN_CONFIG_IGNORED`;不读取 sourceEnv,也不回写配置。 请从仓库删除这些字段,后续 schema 会移除兼容。 ### 3.1 配置发现 省略 `--config` 时,CLI 从 cwd 扫描到仓库边界并收集全部 `super.json`: - 0 份:`CONFIG_NOT_FOUND`。 - 1 份:选中它。 - 多份:`CONFIG_AMBIGUOUS`,列出所有候选,不猜“最近”的一份。 一个仓库有多个产品时,保留根 `super.json`,其他产品使用同目录的显式文件,例如: ```bash supercli build-all --config ./super.superbrowser.json --output-dir .super/superbrowser ``` 显式文件不存在、不可读或无效时绝不回退到默认配置。嵌套默认配置会遮蔽身份, 因此作为冲突拒绝。每个配置驱动的人类输出与 JSON 信封都回显规范 `configPath` 和 `spid`。 ### 3.2 旧配置升级 v5 是破坏性升级,不提供迁移命令或双读。请手工把配置改为 schema 5:删除 root/project `sdk`, 把 SDK/toolchain 设置迁回项目自身文件,并为每个项目填写 `{}` 或完整 build。新 CLI 只发现 `super.json`;其他旧文件名不会被读取。v4 build output 也不能用于新 deploy,必须重新构建。 ## 4. 初始化与项目登记 ### 4.1 `init` ```bash supercli init [dir] [--config ] [--name ] ``` 已有配置时,`init` 校验它并铺设共享文件。无配置时,只有交互式 `init` 会一次性询问 SPID 并原子生成有效配置。`--no-input`、`--output json` 或非 TTY 模式不会提示,必须预先提供 有效配置。bootstrap 输入不是持续 CLI selector。 ### 4.2 `add` 与 `register` `add ` 创建新项目;`register ` 登记已有工程。两者都支持 `--config`, 都要求 `--project`。site 或 service 初始化目标时,`--env-target/--slug/--env` 必须三者同有同无。 Android 必须显式给 `--package`和所需名称;iOS 必须给 `--bundle-id`。身份不从目录名、 Manifest 或模板占位值推导。`add` 只支持 android/ios/site,并随项目模板写入完整 build; `register` 支持四种类型,但只登记 `"build": {}`,不检查或改写现有工程、不生成脚本、也不询问 构建命令。开发者随后必须按实际项目补全 build,否则 build 明确返回 `BUILD_NOT_CONFIGURED`。 所有目录、projectId、类型和平台身份冲突在任何写入前失败。 ## 5. 构建 ```bash supercli build --project web --output-dir .super/build [--config ] supercli build-all --output-dir .super/build [--config ] ``` `build` 只构建一个 `projectId`;`build-all` 按配置顺序串行处理所有项目。CLI 对所有项目只做同一件事: 在项目目录以 `shell:false` 直接执行 `build.entry`,把 `build.args` 中的 `${spid}` 替换为选中配置的 SPID,然后收集声明的 artifacts。它不知道 Gradle、Xcode、npm 或 `docker build`,也不猜产物后缀。 官方模板的 entry 是带 shebang 与执行位的 `scripts/build.sh`,因此官方 build 只承诺 POSIX;iOS 还要求 macOS/Xcode。Windows 可使用项目自带的原生 executable,但 CLI 不把 `.sh` 猜成 bash。 每项目默认超时 1800 秒,可用 `--timeout-seconds 1..86400` 调整。 输出目录的 `super-build-manifest.json` 是 strict manifest v2,绑定配置 SHA-256、SPID、source revision、 projectId/type、声明顺序、安全 portable 路径及复制后 checksum,不再包含 builder evidence。任一项目失败 都会在 manifest/summary 中如实表示;build-all 会继续后续项目。输出目录必须原先不存在,并通过相邻 staging 原子安装。 ## 6. 部署 ```bash supercli deploy --project web --env-target prod --build-dir .super/build supercli deploy-all --env-target prod --build-dir .super/build ``` `deploy` 必须给 `--project/--env-target/--build-dir`;`deploy-all` 必须给 `--env-target/--build-dir`。 缺少必填选项是 parse-time `USAGE_ERROR`,不解析配置、不登录、不产生 deploy result。 参数完整后,CLI 先按选中 v5 配置验证 manifest schema、配置摘要、SPID、project/type、artifact id/kind/path 声明、路径边界和 checksum, 再读凭据或访问网络。`deploy-all` 先对整批完成 preflight: - Android/iOS 记 `unsupported`,不算漏配。 - 有 deploy adapter 的项目漏配 target 记 `not-configured`。 - 构建失败、产物缺失或完整性错误保留精确状态。 - 任一可部署项目 preflight 失败或零可部署项目时,整批零发布副作用。 远端发布按配置顺序串行;首错停止,剩余项目记 `not-run`。无任何成功就失败时 overall=`failed`; 已成功后再失败时 overall=`partial`,两者都非零退出。结果原子写入 `/.super/deploy-results/.json`,永不回写 `super.json`。 成功的 Site 项目与成功或仍在运行的 Service 项目会在 `projects[].resource` 中返回服务端权威地址。旧字段 `domain` 继续是平台主域名字符串;新增的 `domains` 是 schema v1 的可忽略加法字段: ```json { "domain": "api-ab12cd34.supersdk.run", "domains": [ { "domain": "api-ab12cd34.supersdk.run", "url": "https://api-ab12cd34.supersdk.run", "kind": "platform", "status": "ready", "primary": true }, { "domain": "api.example.com", "url": "http://api.example.com", "kind": "custom", "status": "ready", "primary": false } ] } ``` 平台项固定排第一且是唯一 `primary`,可能是 `ready` 或 `pending`;当前 SuperRun 自定义域名保持 服务端返回的 HTTP URL,并且只有已经确认 ready 的绑定会进入列表。pending 或 active-but-unconfirmed 的绑定仍通过 custom-host 管理 API 查看,不会混入发布卡片地址。SuperSite 会从目标环境的 Site detail 稳定回填旧 `domain`,不再依赖真实 deploy DTO 中不存在的顶层 domain。 **命令规范**:跨资源的工作区动词(`init` / `add` / `register` / `build` / `deploy` / `deploy-all`)按 `super.json` 的 `projectId` + `envTarget` 工作,站点和服务都走同一条 `deploy`;资源存在之后的运维 动作按资源分组(`site` / `super-run`),子命令名与 `--env` 用法两组对齐。`publish-site` 是 `deploy` 的弃用兼容名(参数相同、只允许 site),已从 `--help` 隐藏,老脚本仍可运行,请迁到 `deploy`。 服务 deploy 默认等待本次 revision 的镜像 digest、revision ID、active 状态和 synced 发布指针一致。 前台 20 秒未就绪会继续由平台收敛,CLI 继续查询;CLI 等待预算耗尽返回非零退出和 `RUN_DEPLOY_WAIT_TIMEOUT`,结果保留 revisionId/imageDigest/pending,不能视为发布成功。 使用 `super-run show` / `super-run revisions` 跟踪;重新执行 deploy 时复用相同 `--idempotency-key`。 同一幂等键绑定原 revision 和镜像,不能拿它发布另一镜像。 #### 站点发布:异步受理 + 状态机 + `edge_ready`(最低 SuperCLI 版本 0.5.0) 🔴 **站点发布的最低 SuperCLI 版本是 0.5.0**。服务端自本契约起对 `POST …/deploys` 返回 **HTTP 202 受理** (不再是同步完成),旧版 CLI 会把这份受理回执当成"已发布"——CI 会误判为绿。请先升级。 一次站点 `deploy` 分两段计时、四步推进: 1. **上传**:把 zip 传给平台。上传阶段的超时随包体积增长(300 秒 + 每 256 KiB 加 1 秒,50 MiB 的包 ≥ 8 分钟), 不再是单个固定 60 秒 abort;这段时间**不消耗**后面的等待预算。 2. **受理(202)**:平台校验通过即返回 `deployId`(`dep_…`)和 `state: accepted`。CLI **立即**在 stderr 打一行进度 `deploy accepted: deployId=dep_… state=accepted`(`--output json` 下 stdout 仍只在终态输出一份 result)。 受理前就能判定的问题(坏包 / 缺 `index.html` / 版本未递增 `VERSION_NOT_INCREMENTED` / 站点删除中 …)沿用原错误码, 不产生 deploy 记录。 3. **等待**:每 3 秒查一次 `GET …/deploys/{deployId}`,直到终态或等待预算耗尽。默认等 **420 秒(7 分钟)** ≥ 服务端 5 分钟处理时限 + 边缘收敛余量;`--wait-timeout ` 可调(1..3600)。**没有 `--no-wait`**。 4. **终态**:只有 `edge_ready` 才是成功。 | `state` | 含义 | CLI 行为 | |---|---|---| | `accepted` | 已受理,作业未开始 | 继续等待 | | `processing` | 作业进行中(构建离线包 / 上传对象) | 继续等待 | | `committed` | 数据库已切换当前版本,边缘尚未确认 | 继续等待,**绝不**当成功 | | `edge_ready` | 边缘已探测到本次 `deployId` 在线 | `ok:true`,exit 0 | | `failed` | 作业失败(含平台副本丢失 `DEPLOY_WORKER_LOST`、处理超时 `DEPLOY_PROCESSING_TIMEOUT`) | `ok:false`,`error.code` / `error.retryable` 原样透出,非零退出 | | `superseded` | 在生效前被更新的版本取代 | `ok:false`,`SITE_DEPLOY_SUPERSEDED`,`retryable:false`,`resource.supersededBy`(`null` = 站点已删除),非零退出 | 等待与冲突的处置规则: - **等待超时**(`SITE_DEPLOY_WAIT_TIMEOUT`,`retryable:true`,`resource.{deployId,state,pending:true}`):非零退出, 但发布**可能仍在推进**。用 `supercli site deploy show --env ` 继续查询这个 `deployId`; **不要为了看状态新发一版**。若最终 `failed` 再按下一条处理。 - **`failed` 且 `error.retryable: true`**(如 `STORAGE_UNAVAILABLE` / `DEPLOY_WORKER_LOST`):用**同一个** `--idempotency-key` 重放 `deploy`,失败的记录会释放该键,平台重新受理一次新的发布。`retryable: false` 的失败先修原因再发。 - **`SITE_DEPLOY_SUPERSEDED`**:不重试。线上是取代者 `supersededBy`;`supersededBy` 为 `null` 表示站点已被删除。 - **`DEPLOY_IN_PROGRESS`**(同一站点环境已有在途发布,`extra.resource.deploy_id` 是在途 id):CLI 不换 key、不立刻失败, 先轮询在途 id 直到它离开 `accepted`/`processing`(到 `committed` 或 `failed` 即可),再用同一幂等键重试一次 POST; 仍冲突则以 `DEPLOY_IN_PROGRESS` 非零退出。 - **同键重放**:平台以 `x-idempotency-key` 落库去重。同键同内容返回原 `deployId`(`idempotent_replay: true`, CLI 直接进入等待/终态判定);同键不同内容返回 `IDEMPOTENCY_CONFLICT`,换键。 - `committed`、旧版 `503 POINTER_SYNC_PENDING` 形态、任何 `pending` 形态**任何情况下都不会输出 `ok:true`**; 只可能继续等待,或以非零退出结束。 **`resource` 字段对照(deploy result `projects[].resource`,两种 target 都是驼峰)**: | 字段 | site(SuperSite) | service(SuperRun) | 说明 | |---|---|---|---| | `id` | 站点 id | 服务 id | 服务端数值 id | | `domain` / `domains` | 平台域名(终态 `ready`) | 平台域名 + 已确认自定义域名 | 见上文 domains 说明 | | `deployId` | `dep_…` | — | 受理即返回;超时/失败/被取代时同样保留 | | `state` | 六态之一 | — | 成功恒为 `edge_ready` | | `supersededBy` | 取代者 `deployId` 或 `null` | — | 仅 `superseded` | | `error` | `{code, message, retryable}` | — | 仅 `failed`,来自服务端 | | `edge` | `{lastProbeAt, lastOutcome, observedDeployId}` | — | 服务端最近一次边缘探测 | | `pending` | `true` 仅出现在超时/在途失败的 resource 上 | `true` 表示仍在 provisioning | 站点成功结果不带 | | `revisionId` / `imageDigest` / `status` | — | 本次 revision / 镜像 / 服务状态 | 服务专有 | `site deploys` / `site deploy show` 这两条**读**命令则原样透传服务端 snake_case(`deploy_id` / `state` / `superseded_by` / `pointer_sync_state` …),与 `site show` 一致;驼峰只出现在 `supercli deploy` 的 result 里。 ### 6.1 发布之后:`site` 与 `super-run` 服务 `deploy` 只给已有服务发布镜像(`--image-only` 是同义显式选项),不创建服务、不修改 ENV、 密钥、端口、资源或绑定。未创建返回 `RUN_SERVICE_NOT_CREATED`;先用 Console 或 `super-run create`。 目标未声明 port 时沿用平台值;声明与平台端口不一致返回 `RUN_PORT_MISMATCH`。 需要调整端口时先执行 `super-run update --service --env --port `。 旧 `--env-var`/`--secret` 部署参数会被拒绝,请使用独立配置命令。 Site 发布仍可创建站点,Site 生命周期命令须带 `--env`。 ```bash # 静态站点(站点是环境级实体:一个站点只属于一个环境,所有子命令都要 --env; # slug 在项目内唯一,跨环境不能重名——staging 建了 docs,prod 就不能再建 docs) supercli site list --config ./super.json --env prod supercli site show docs --config ./super.json --env prod supercli site disable docs --config ./super.json --env prod supercli site enable docs --config ./super.json --env prod supercli site deploys docs --config ./super.json --env prod # 每条带 state supercli site deploy show docs dep_xxx --config ./super.json --env prod # 只读:按 deployId 看状态机 supercli site rollback docs --config ./super.json --env prod --deploy-id dep_xxx supercli site delete docs --config ./super.json --env prod # 容器服务:显式创建(镜像需已存在于 registry,digest 必须是完整 sha256) supercli super-run create api --env prod --image-ref "$IMAGE_REF" --image-digest "$IMAGE_DIGEST" --port 8080 # ENV/secret/资源变更与发布分开执行;专用命令不把配置写回 super.json supercli super-run env-vars set api --env prod --set APP_MODE=production supercli super-run update --service api --env prod --cpu-milli 500 --mem-mib 512 supercli super-run list --config ./super.json --env prod supercli super-run show api --config ./super.json --env prod supercli super-run disable api --config ./super.json --env prod supercli super-run revisions api --config ./super.json --env prod supercli super-run rollback api --config ./super.json --env prod --revision-id rev_xxx supercli super-run delete api --config ./super.json --env prod supercli super-run update api ... # 改规格,见 --help supercli super-run logs api --config ./super.json --env prod --follow # 顶层 `supercli logs` 同义 # 环境变量(增量语义,见下) supercli super-run env-vars get api --config ./super.json --env prod supercli super-run env-vars set api --config ./super.json --env prod \ --set LOG_LEVEL=debug --set-secret API_KEY=xxx --unset OLD_FLAG # 自定义域名 supercli super-run custom-host list api --config ./super.json --env prod supercli super-run custom-host add api --config ./super.json --env prod --hostname app.example.com supercli super-run custom-host verify api --config ./super.json --env prod --hostname app.example.com supercli super-run custom-host remove api --config ./super.json --env prod --hostname app.example.com ``` `custom-host add` / `verify` 成功时都会带回两条 DNS 记录(恒 2 条,TXT 在前):一条 `TXT` 记录证明域名 所有权,一条路由记录(`CNAME`)把流量指到平台——两条都是可以直接复制粘贴到 DNS 面板的完整 FQDN / 记录值,无需自己拼接。`verify` 只校验 TXT 所有权记录,路由记录不参与判定。失败时按原因分两类,文案可区分: TXT 记录名或值不对(需要回 DNS 面板核对 TXT 后重试)与 DNS 解析本身失败(超时/SERVFAIL/传输错误等,此时尚无法判定记录对错,等一会儿重新 `verify` 即可)。若这次 `verify` 从另一个服务手里夺回了同一个域名,输出会额外打印一行警示,带上被夺 服务被脱敏后的名字(首字符 + `***` + 末字符,恒 ≤5 字符,如 `a***z`)。 🔴 **`env-vars set` 是增量,不是替换**。服务端那条路由收的是**全量 desired state**——请求里没有的 变量会被删除。所以 CLI 先读一次现有变量,再把没动过的键以「省略 value」的形式回写(这正是服务端 的保留哨兵),只有你显式 `--set` / `--set-secret` / `--unset` 的键才变。密钥读回来恒为 `********`, **CLI 绝不会把这串掩码当字面量写回去**。密钥只能升不能降:已经是密钥的键不能用 `--set` 改回明文, CLI 会在本地拦下来并告诉你用 `--set-secret`。输出只报改了哪些**键名**,不回显任何值。 **`site deploy show --env ` 是只读命令**:只发一次 GET,不带 `x-idempotency-key`,不会触发也不会 重放任何发布;输出服务端 `DeployView` 原样字段(`deploy_id` / `state` / `is_current` / `pointer_sync_state` / `edge` / `error` / `superseded_by` / `processing_at` / `committed_at` / `edge_ready_at` …)。`deployId` 不属于该站点环境时返回 `NOT_FOUND`。`site deploys` 列表每条同样带 `state`。CI 里遇到 `SITE_DEPLOY_WAIT_TIMEOUT` 用它继续盯同一个 `deployId`,而不是再发一版。`site rollback` 在有在途发布时同样返回 `DEPLOY_IN_PROGRESS`(带在途 id),等它离开 `accepted`/`processing` 再回滚。 🔴 **`site delete` / `super-run delete` 是比较并交换**:服务端要求调用方报出「我以为要删的那个 id」, 所以 CLI 会先读一次再删。删除与读取之间被同名重建的资源会被拒绝,而不是被误删。 ## 7. 账号、凭据与权限 ```bash supercli login --environment prod supercli accounts supercli projects supercli switchaccount prod:1042 supercli logout supercli update --channel main ``` `login` 通过浏览器授权码换取用户 token,CLI 不接触密码。默认凭据存系统钥匙串; 只有显式 `--credential-backend encrypted-file` 才使用口令加密文件。无安全后端时 fail-closed, 不保存明文 token。 CI/Agent 应使用 `SUPERCLI_TOKEN`。`sst_` 服务 token 和 `cli_` 用户 token 两种都可以。 `cli_` 需要真人一次性批准;CI 推荐 `sst_`,因为不需要真人批准。 两种 token 的权限都等于签发者在所属组织中的角色。凭据不能创造凭据,也不能创造权限。 不要把 token 放到 argv。 🔴 **CLI 不选择组织**。组织在 token **签发时**就绑定好了,服务端只从 `token.org` 取;客户端 再送一个 org 只会「一致(多余)」或「不一致(403)」。因此 `--org` 已**弃用并忽略**(deploy / publish-site 兼容名 / logs / image / super-run),`super.json` 的 `orgId` 同样不再被消费(仍接受, 避免既有配置校验失败)。要换组织,就换一个绑定到那个组织的凭据。 例外是 `supercli org` 那组命令:那里组织是**被查询的对象**而不是路由参数,id 也是问服务端要的。 `sst_` 与 `cli_` 现在**能到达的路由完全一致**:过去只有 CDN 上传对 `sst_` 单独设禁,但服务令牌本 就以签发者身份、按签发者的角色运行,这条例外没有隔离出任何东西,只会让 CI 撞上一个意外的 403, 已经移除。未来会做成按 token 可配置的能力集。 ```bash export SUPERCLI_TOKEN=sst_... export SUPERCLI_API_ORIGIN=https://console.superplatform.ai supercli deploy --project web --env-target prod --build-dir .super/build \ --output json --no-input ``` ### 7.0 多账号:一份凭据 = 一个身份 **supercli 始终对应一个账号和一个 token,代表这个凭据的身份与权限。** 组织不是 CLI 选的 (见上),所以「同一个人管理多个组织」在 CLI 这边就是「持有多份凭据」。 用 `--as` 给凭据起名,它们就能并存: ```bash supercli login --as acme --environment prod supercli login --as globex --environment prod supercli accounts # * 标当前默认 supercli switchaccount prod:1042:acme # 改默认 supercli projects --account globex # 只这一次用另一个身份 ``` `--as` 的标签是 1-32 位小写字母、数字或连字符。不带 `--as` 登录派生的 key 与以前完全一致, 既有 `accounts.json` 不受影响、无需迁移。 **身份优先级(越显式、越局部越优先)**: ``` --account ← 最高:这一次调用用谁 SUPERCLI_TOKEN ← 其次:环境给的身份 accounts.json 的 current ← 兜底:switchaccount 设的默认 ``` 🔴 **`--account` 解析失败一律报错退出,绝不回退**到 `SUPERCLI_TOKEN` 或 current。回退会在你 以为用着 A 的时候拿 B 把命令跑了 —— 那才是真正难查的事故。selector 支持账号 key / 标签 / email / userId,匹配到多个会要求你写全 key,**不猜**。 `--account` 只收**标识符**,不收 token(和「没有 `--token` flag」是同一条纪律:secret 不上 argv)。要用 token 就用 `SUPERCLI_TOKEN`。同时出现时 `--account` 赢,并在 stderr 说明一次。 ### 7.1 `projects`:查当前凭据能看到哪些项目 `projects` 列出**当前凭据所属组织**下的全部项目,每行 `spid` + 项目名。这里的 `spid` 就是 `super.json` 的 `spid` 字段、也是 `supercli init` 交互式追问的那个值 —— 不用再开控制台抄。 它是账号级命令:**不读 `super.json`,没有 `--config`/`--spid`**,因为结果只取决于你是谁。 组织由凭据自身携带,CLI 不发送 `X-Org-Id`。member 与 admin 都可读。 ```bash supercli projects supercli projects --keyword demo supercli projects --output json --no-input ``` human 模式逐行输出 `spid 项目名`,被停用的项目带 `(disabled)` 后缀。JSON 模式的 `result` 形如 `{"total": 3, "truncated": false, "projects": [{"spid","name","description","status","created_at","updated_at"}]}`; `status` 为 `1` 启用 / `0` 停用。CLI 内部自动翻页取全量,不需要你自己分页;极端数量下命中列表上限时 `truncated` 为 `true`(**不会静默截断**)。凭据失效为 `AUTH_INVALID` / exit 3,不属于任何组织为 `FORBIDDEN` / exit 2。 ### 7.2 项目生命周期 裸 `supercli projects` 仍然是列表;增删改挂在它下面作为子命令,同为账号级、同样不读 `super.json`。**创建 / 修改 / 删除都要求凭据在该组织内是 admin**。 ```bash supercli projects create --name "Acme App" --description "线上主 App" supercli projects show ab12cd34 supercli projects update ab12cd34 --name "Acme" supercli projects update ab12cd34 --clear-description supercli projects update ab12cd34 --status 0 # 停用 supercli projects delete ab12cd34 # 软删除 ``` `create` 返回的 `spid` 就是接下来写进 `super.json` 的值。`update` 的语义是「只改你给的字段」: 不传 `--description` 表示保持原样,`--clear-description` 才是清空,两者互斥。一个字段都不给会本地 报 `USAGE_ERROR`,而不是发一次什么都不改的请求。`--status` 只接受 `1` / `0`。 ### 7.3 `org`:组织信息与成员名单 ```bash supercli org list supercli org show supercli org members supercli org rename --name "Acme Inc" # 需要 admin ``` token 与它所属的组织一一绑定,所以 `show` / `members` / `rename` 省略 `--org` 时会自动解析成 该组织,不需要你去哪里抄一个数字 id;确实能看到多个组织时才要求显式 `--org `。 🔴 **成员治理不在 CLI 内,也不会加进来**:建组织、发邀请、改成员角色、移除成员、接受邀请这五条 路由是**仅浏览器会话**可达(服务端 `CREDENTIAL_GUARDED_ROUTES`)。判据是「用了它会留下撤销 token 也收不回的访问权」—— 成员行比签发它的 token 活得久,所以凭据不得铸造成员资格。同理, **API Token 自身的签发/查看/吊销**与 **CLI 授权批准**也只在控制台。这不是 CLI 尚未实现,是平台 刻意关闭:给这些动作做命令只会稳定地得到 401。 **归因(attribution)管理**目前同样是仅会话可达:渠道、Webhook 配置与归因记录查询共 10 条路由都在 禁区内。与 token 无关,是产品侧决定;需要时请在控制台操作。 ## 8. 其他配置驱动命令 analytics、db、env、image、logs、settings、scheduler 都支持 `--config ` 并从配置读 SPID。它们不需要 `--project`;原有业务选择器保持不变: - analytics/db/settings/scheduler 保留 `--env`。 - db bind/unbind/refresh 保留 `--service`;db query/execute 的 `--conn` 可选。 - image ls/rm 保留 repository/tag 位置参数。 - logs 保留 service 位置参数和 `--env`。 - env list/create/delete 只需 `--config`。 ```bash supercli env list --config ./super.json supercli env create qa --config ./super.json supercli env delete qa --config ./super.json supercli settings list --config ./super.json --env prod supercli settings get feature_flags --config ./super.json --env prod supercli settings set feature_flags --config ./super.json --env prod \ --content 'return { checkoutV2: true };' supercli settings debug feature_flags --config ./super.json --env prod supercli settings debug feature_flags --config ./super.json --env prod \ --file ./candidate.js --mock-params '{"uid":7}' supercli settings history feature_flags --config ./super.json --env prod supercli settings rollback feature_flags --config ./super.json --env prod --history-id 12 supercli settings delete feature_flags --config ./super.json --env prod ``` 环境名必须是 1-10 位小写字母或数字;`prod` / `stg` / `quarantine` 为平台保留字。一个项目最多 5 个环境(含 prod),内置 `prod` 不可删除,`delete` 是软删除。**创建和删除环境需要凭据持有者 在该组织内是 admin**:member 凭据得到 `FORBIDDEN` / exit 2。非法名字在本地零请求拒绝。 settings `get` 返回完整记录,包括原始 js_content。set 只接受 `--content` 或 `--file` 二选一; 重复创建同 key 会返回 `SETTINGS_KEY_EXISTS`,并发修改会返回 `SETTINGS_CONFLICT`。组织 member 可读, admin 可写;member set 为 `FORBIDDEN` / exit 2。`--output json --no-input` 下缺参数为 `USAGE_ERROR` / exit 6。 不要把敏感内容直接放在 `--content` 里,它可能进入 shell-history;使用权限受控的 `--file`。 `debug` 在服务端沙箱里跑一次**但不发布**:省略 `--content`/`--file` 就是拿线上那份跑,加上它们 则只对这一次运行生效;`--mock-params` 覆盖存着的 mock 参数,必须是合法 JSON(本地校验)。因为 不落任何东西,它可以安全地指向 prod。`history` 列出可回滚的修订,`rollback --history-id` 取的就是 那里的 id。`delete` 是软删除。`delete` / `rollback` 需要 admin,`debug` / `history` member 即可。 ### 8.1 SuperScheduler 定时 HTTP(S) 环境级 cron Job:平台按时刻向你指定的公网 HTTP/HTTPS 发请求。能力始终开启,不需要单独启用。 最短间隔 1 分钟。Job 不是部署单元;改配置只能删了再建。HMAC 密钥只在 `create` 成功时出现一次, 列表和详情永远不回 Header 或 secret。 ```bash supercli scheduler list --config ./super.json --env prod supercli scheduler create extract-her --config ./super.json --env prod \ --schedule '* * * * *' \ --timezone Asia/Shanghai \ --url https://api.example.com/extract \ --method POST \ --header-env Authorization=EXTRACT_TOKEN \ --body-file ./extract.json supercli scheduler show extract-her --config ./super.json --env prod supercli scheduler pause extract-her --config ./super.json --env prod supercli scheduler resume extract-her --config ./super.json --env prod supercli scheduler run-now extract-her --config ./super.json --env prod supercli scheduler executions extract-her --config ./super.json --env prod supercli scheduler delete extract-her --config ./super.json --env prod ``` `--schedule '* * * * *'` 就是每分钟一次,这是平台允许的最短间隔。时区必须是 IANA 名(`UTC`、 `Asia/Shanghai`)。`--header` 会进 shell-history,敏感头只用 `--header-env Name=VAR`。`--body` 同样可能进 history,敏感或较长的 payload 用 `--body-file`。默认 HMAC 开启;目标侧用 `X-SuperSDK-Timestamp` / `X-SuperSDK-Signature` / `X-SuperSDK-Job` 校验。`--no-hmac` 才关掉。 `http://` 会明文传输 Header、Body 和 HMAC,human 模式会警告。 `run-now` 会同步等到这次出站(含合法重试)结束,不改时刻表。组织 member 可读列表/详情/执行记录; 创建、暂停、恢复、立即触发、删除需要 admin。配额每环境 20 条(含 paused)。名字规则与 SuperDB 连接名相同:1-40 位小写字母、数字或连字符,首尾不能是连字符。不合规、缺 `--env`、GET 带 body 都在本地零请求拒绝。`--output json --no-input` 下缺参数为 `USAGE_ERROR` / exit 6。 | 能力 | 组织 member | admin | Agent / CI | |---|---:|---:|---:| | `scheduler list` / `show` / `executions` | ✅ | ✅ | ✅ | | `scheduler create` / `pause` / `resume` / `run-now` / `delete` | ❌ | ✅ | ✅,凭据须为 admin | ## 9. 数据库与事件分析 ```bash supercli db list --config ./super.json --env prod supercli db query --config ./super.json --env prod --conn reporting \ --sql 'SELECT id, email FROM users' supercli db query --config ./super.json --env prod --file ./query.sql cat ./query.sql | supercli db query --config ./super.json --env prod supercli db execute --config ./super.json --env prod --conn reporting \ --sql 'UPDATE users SET active = true' supercli db execute --config ./super.json --env prod --yes --file ./maintenance.sql # 连接管理(首次引导:先 env create,再在该环境里登记连接) supercli db conn create --config ./super.json --env qa \ --name reporting --kind postgres \ --host db.internal.example --port 5432 --db-name appdb --username svc \ --password-env DB_PASSWORD --ssl-mode require supercli db conn show reporting --config ./super.json --env qa supercli db conn test reporting --config ./super.json --env qa supercli db conn tables reporting --config ./super.json --env qa supercli db conn columns reporting orders --config ./super.json --env qa supercli db conn update reporting --config ./super.json --env qa \ --kind postgres --host db2.internal.example --port 5432 \ --db-name appdb --username svc --ssl-mode require # 只查看删除风险和当前已知使用方,不删除 supercli db conn delete reporting --config ./super.json --env qa # 接受风险后确认删除;引用不会阻塞删除 supercli db conn delete reporting --config ./super.json --env qa --yes # 删除只移除平台登记和保存的凭据;不会删除外部数据库或撤回运行实例已有凭据。 # 后续部署、启用或刷新可能因引用失效而失败,使用方清单不穷尽历史实例。 # 执行审计 supercli db sql-executions --config ./super.json --env qa supercli db sql-executions 9 --config ./super.json --env qa supercli analytics query --config ./super.json --env prod \ --from 2026-08-01 --to 2026-08-10 --sql 'SELECT event_name FROM events' supercli analytics tables --config ./super.json --env prod supercli analytics table events --config ./super.json --env prod supercli analytics columns events --config ./super.json --env prod # 非 SQL 面:事件目录、控制台自己的查询、看板、审计 supercli analytics status --config ./super.json --env prod supercli analytics events --config ./super.json supercli analytics properties purchase --config ./super.json supercli analytics public-properties --config ./super.json supercli analytics property-values purchase currency --config ./super.json --limit 50 supercli analytics sync --config ./super.json supercli analytics events-query --config ./super.json --env prod --file ./query.json supercli analytics retention --config ./super.json --env prod --file ./retention.json supercli analytics dashboard list --config ./super.json supercli analytics dashboard create --config ./super.json --name 漏斗 --file ./snapshot.json supercli analytics dashboard update 3 --config ./super.json --name 漏斗 --file ./snapshot.json supercli analytics dashboard delete 3 --config ./super.json supercli analytics sql-executions --config ./super.json --env prod ``` SQL 必须且只能来自 `--sql`、`--file` 或非 TTY `stdin` 三种来源之一。零来源、多来源、空 SQL 或 不可读文件都在本地零请求失败。`db query` 只读;`db execute` 只允许真人 admin。普通 SQL 发 `confirm: false`,危险 SQL 必须显式 `--yes`才发 `confirm: true`。Agent/CI 中执行写入本地返回 `DB_EXECUTE_REQUIRES_HUMAN`,不发网络请求。 这条边界在 scoped token、幂等写入、plan/dry-run 与 before/after audit 齐备前保持 fail-closed; 被本地拒绝的 Agent 写入不会发网络请求。 `db sql-executions` 读的是这个环境的 SQL 执行审计(含控制台里跑过的),非 admin 只看得到自己的行; 带 id 读单条。列表走 `/cli/` 那张脸,单条只存在于共享那张脸上——同一张审计表,两条路由的差别只在 放行哪类凭据。 🔴 **`db conn create` 以真实连通性为前置**:服务端在落库之前会真的连一次目标库,连不上就 **不创建**(返回 `DB_TARGET_TIMEOUT` / `DB_TARGET_UNREACHABLE` 等)。所以建连接前先确保平台 出口 IP 能到达目标——出口 IP 可以先用 `db conn test` 看已有连接拿到,或问管理员。 连接名规则与服务端一致:**1-40 位小写字母、数字或连字符,首尾不能是连字符**。不合规在本地零 请求拒绝。 🔴 **口令只走环境变量名,永远不进 argv**。`--password-env ` 接收的是**变量名**而不是口令 本身——放进命令行会同时写进 shell history 和每一次 `ps` 输出。`db conn update` 省略 `--password-env` 表示沿用已保存的凭据(服务端重新加密它已持有的那份),不是清空。 `--ssl-mode disable` 会让凭据和每条查询明文过网,必须同时给 `--confirm-plaintext` 才放行, 否则本地零请求拒绝。`db conn test` 回答「平台能不能连上」:连不上仍是**成功的命令 + 否定的答案** (`ok: false` + `error_code`,exit 0),并返回需要加白的平台出口 IP;只有 CLI 自己够不到 Console 才是失败退出。 | 能力 | 组织 member | admin | Agent / CI | |---|---:|---:|---:| | `db list` / `db query` | ✅ | ✅ | ✅ | | `db conn show` / `tables` / `columns` | ✅ | ✅ | ✅ | | `db conn create` / `update` / `delete` / `test` | ❌ | ✅ | ✅,凭据须为 admin | | `db execute` | ❌ | ✅,仅 human | ❌ | | analytics 查询/元数据 | 按项目权限 | 按项目权限 | ✅,只读 | `events-query` / `retention` / `dashboard create|update` 的请求体是一套**字段很多的 DSL**, 用命令行开关表达只会做出没人记得住、且每次服务端加字段就漂移的接口,因此它们的 body 走 `--file ` 的 JSON 文件。🔴 **项目身份始终以 `super.json` 为准并覆盖文件里的 `spid`**—— 从别的项目复制来的 body 不会悄悄跑到当前项目上。`dashboard update` 是整体覆盖 name + snapshot, 不是合并。 `analytics events` 这一族路由的 spid 走 **query 参数**(不在路径里),所以它们是配置驱动命令; `sql-executions` 是项目级审计(路径带 spid/env),与 `/cli/` 面上那份 CLI 自己的执行记录不是同一张表。 ### 9.1 CDN 与短链 ```bash supercli cdn ./logo.png --config ./super.json --env prod supercli shortlink list --config ./super.json supercli shortlink create --config ./super.json --url https://example.com/landing supercli shortlink delete sl_xxx --config ./super.json ``` `cdn` 是 CLI 唯一一条 `multipart/form-data` 请求:只传一个名为 `file` 的字段,资源名取文件的 basename,多一个字段服务端就整体拒绝。命令自己的 request id 兼作 `Idempotency-Key`,重试同一次 调用不会产生两份资源。上传需要 admin;`sst_` 服务令牌**现已与 `cli_` 同权**(旧的 CDN 专属禁令已 移除,见下)。 CLI 只消费按 SPID/env 定界的 Console API,不下发密码、DSN、BigQuery dataset 或物理 project_id。敏感路径中的 Bearer token、`rows[*][*]`、JSON/JSONB、SQL、上游原始 message 和 凭据值都 fail-closed 脱敏;只报 `credential_masked` 等允许字段。 ## 10. Agent Contract 与退出码 `--output json` 隐含非交互,也可显式加 `--no-input`。stdout 恰好一行 JSON: ```json {"schema_version":1,"ok":true,"result":{},"request_id":"cli_..."} ``` 过程日志只去 stderr。`request_id` 与请求 `X-Request-Id` 保持一致。机器输出和 deploy result 都不保存 token、secret、runtime env 值、Docker credential 或任意响应 body。 | 退出码 | 类别 | 处理 | |---:|---|---| | 0 | 成功(服务 deploy 含已受理的 soft success;站点 deploy 只有 `edge_ready`) | 继续 | | 2 | 业务/权限,如 `FORBIDDEN`、`NOT_FOUND` | 修参数或权限,不盲目重试 | | 3 | `AUTH_INVALID` | 更换 token 或重新登录 | | 4 | 限流 | 有界重试 | | 5 | 网络/5xx/非预期响应 | 按幂等边界重试 | | 6 | `USAGE_ERROR` | 修正命令行 | 配置发现、manifest 完整性和 deploy-all preflight 都在凭据读取/网络前执行。选择歧义、配置漏项、 旧选项或未知字段都明确失败,不回退、不猜测、不静默跳过。 ## 11. 发布域名与故障处置 SuperSite/SuperRun 的域名以服务端返回值为准,CLI 不自行拼域名。常见处置: - Site 平台域名取目标环境扁平 DTO 的 `site.domain`;`site show --env` 返回单环境对象(含 `env/domain/current_deploy_id/pointer_sync_state`),不含 `environments[]`。旧无 env API 请求返回 `UPGRADE_REQUIRED` 并提示升级 CLI 至至少 0.3.12。Run 平台域名取当前 service/env detail,自定义域名取同一 path-scoped custom-host list。 - CLI 校验域名、URL、协议和敏感值后才写入结果;非法项宁可不返回,也不会用 slug/env/zone 猜测。 - 平台地址使用 HTTPS;当前 SuperRun 自定义绑定按服务端能力原样使用 HTTP,不自动升级协议。 - 401:重新登录或更换 token。 - 403:当前身份不在所选配置的组织或不是 admin。 - 429:稍后有界重试。 - 资源名冲突:只读复查归属,不自动覆盖其他项目。 - 服务发布、`site rollback`、建站遇到边缘指针正在同步(`503 POINTER_SYNC_PENDING`):已受理的 soft success 仍为 exit 0,不要重复创建发布。**站点 `deploy` 不再产生这一形态**:它只按 `deployId` 的状态机判定,`committed` / pending 一律不算成功(见 §6)。 - 站点发布 `SITE_DEPLOY_WAIT_TIMEOUT`:发布可能仍在推进,用 `supercli site deploy show --env ` 继续盯同一个 `deployId`,不要新发一版;确认 `failed` 且 `retryable:true` 后用同一个 `--idempotency-key` 重放。 - 站点发布 `SITE_DEPLOY_SUPERSEDED`:本次在生效前被 `supersededBy` 取代,不重试;`supersededBy: null` 表示站点已删除。 - 站点发布 `failed`:`error.code` / `error.retryable` 来自服务端(`DEPLOY_WORKER_LOST`、`DEPLOY_PROCESSING_TIMEOUT`、 `STORAGE_UNAVAILABLE` 等可重试 → 同键重放;不可重试的先修包/修版本)。 - 站点发布 `DEPLOY_IN_PROGRESS`:CLI 已自动等在途发布离开 `accepted`/`processing` 并同键重试一次;仍报此码说明 在途发布卡住,用 `site deploy show` 看在途 `deployId` 的 `state` / `error`。 - 站点发布 `SITE_DEPLOY_STATE_UNAVAILABLE`:受理响应缺 `deploy_id` 或缺 `state`,说明对端 Console 契约过旧或畸形—— 升级服务端或联系平台,不重试。 - 站点发布 `SITE_DEPLOY_STATE_UNKNOWN`:轮询看到了本地未识别的状态值,说明本地 CLI 版本过旧——升级 CLI。 - 站点发布上传并发上限(`DEPLOY_CAPACITY_EXCEEDED`)现为**每实例并发处理中的部署数**(作业级,单个部署最长约 5 分钟),命中后稍后用同一个 `--idempotency-key` 重试。 - 站点发布等待预算:命中 `DEPLOY_IN_PROGRESS` 时,CLI 先等在途发布离开 `accepted`/`processing`,随后本次上传 再走一次完整等待——两段各自独立消耗一份 `--wait-timeout` 预算,最坏情况下总等待时间约为其 2 倍。 - 边缘响应头 `X-SuperSite-Deploy-Id`:站点静态 200 响应带上当前生效的 `deploy_id`(容器反代、404/410 不带)。 平台据此把 `committed` 推进到 `edge_ready`;排查"CLI 说 edge_ready 但看到旧页面"时对比该头与 `deployId`, 不一致多半是本地/中间缓存。 所有生产部署前建议先运行对应的 `--dry-run`;它可访问 staging/Console 做计划验证, 但不上传站点、不推镜像、不创建资源,也不修改 `super.json`。