@numa-tech/numa 1.13.4 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +140 -26
  2. package/dist/application-onboarding/client.d.ts +1 -0
  3. package/dist/application-onboarding/commands.d.ts +2 -0
  4. package/dist/application-onboarding/commands.js +41 -16
  5. package/dist/application-onboarding/commands.js.map +1 -1
  6. package/dist/application-onboarding/errors.js +1 -1
  7. package/dist/application-onboarding/errors.js.map +1 -1
  8. package/dist/application-onboarding/schemas.d.ts +8 -0
  9. package/dist/application-onboarding/schemas.js +4 -2
  10. package/dist/application-onboarding/schemas.js.map +1 -1
  11. package/dist/application-onboarding/scm-credentials.d.ts +5 -2
  12. package/dist/application-onboarding/scm-credentials.js +64 -13
  13. package/dist/application-onboarding/scm-credentials.js.map +1 -1
  14. package/dist/cli.js +4 -2
  15. package/dist/cli.js.map +1 -1
  16. package/dist/command-catalog.js +252 -15
  17. package/dist/command-catalog.js.map +1 -1
  18. package/dist/integration-bootstrap/client.d.ts +23 -0
  19. package/dist/integration-bootstrap/client.js +124 -0
  20. package/dist/integration-bootstrap/client.js.map +1 -0
  21. package/dist/integration-bootstrap/commands.d.ts +20 -0
  22. package/dist/integration-bootstrap/commands.js +129 -0
  23. package/dist/integration-bootstrap/commands.js.map +1 -0
  24. package/dist/integration-bootstrap/schemas.d.ts +422 -0
  25. package/dist/integration-bootstrap/schemas.js +216 -0
  26. package/dist/integration-bootstrap/schemas.js.map +1 -0
  27. package/dist/jenkins/commands.d.ts +10 -0
  28. package/dist/jenkins/commands.js +15 -0
  29. package/dist/jenkins/commands.js.map +1 -0
  30. package/dist/jenkins-jobs/client.d.ts +12 -12
  31. package/dist/jenkins-jobs/schemas.d.ts +96 -8
  32. package/dist/jenkins-jobs/schemas.js +2 -2
  33. package/dist/jenkins-jobs/schemas.js.map +1 -1
  34. package/dist/pipeline/client.d.ts +5 -0
  35. package/dist/pipeline/commands.js +2 -0
  36. package/dist/pipeline/commands.js.map +1 -1
  37. package/dist/pipeline/schemas.d.ts +6 -0
  38. package/dist/pipeline/schemas.js +2 -0
  39. package/dist/pipeline/schemas.js.map +1 -1
  40. package/dist/platform-build-job-replans/client.d.ts +283 -0
  41. package/dist/platform-build-job-replans/client.js +148 -0
  42. package/dist/platform-build-job-replans/client.js.map +1 -0
  43. package/dist/platform-build-job-replans/commands.d.ts +6 -0
  44. package/dist/platform-build-job-replans/commands.js +371 -0
  45. package/dist/platform-build-job-replans/commands.js.map +1 -0
  46. package/dist/platform-build-job-replans/schemas.d.ts +145 -0
  47. package/dist/platform-build-job-replans/schemas.js +150 -0
  48. package/dist/platform-build-job-replans/schemas.js.map +1 -0
  49. package/dist/platform-build-rollouts/client.d.ts +346 -0
  50. package/dist/platform-build-rollouts/client.js +153 -0
  51. package/dist/platform-build-rollouts/client.js.map +1 -0
  52. package/dist/platform-build-rollouts/commands.d.ts +98 -0
  53. package/dist/platform-build-rollouts/commands.js +465 -0
  54. package/dist/platform-build-rollouts/commands.js.map +1 -0
  55. package/dist/platform-build-rollouts/schemas.d.ts +348 -0
  56. package/dist/platform-build-rollouts/schemas.js +257 -0
  57. package/dist/platform-build-rollouts/schemas.js.map +1 -0
  58. package/dist/platform-builds/client.d.ts +147 -0
  59. package/dist/platform-builds/client.js +115 -0
  60. package/dist/platform-builds/client.js.map +1 -0
  61. package/dist/platform-builds/commands.d.ts +7 -0
  62. package/dist/platform-builds/commands.js +226 -0
  63. package/dist/platform-builds/commands.js.map +1 -0
  64. package/dist/platform-builds/schemas.d.ts +127 -0
  65. package/dist/platform-builds/schemas.js +66 -0
  66. package/dist/platform-builds/schemas.js.map +1 -0
  67. package/dist/publications/client.d.ts +30 -0
  68. package/dist/publications/client.js +6 -1
  69. package/dist/publications/client.js.map +1 -1
  70. package/dist/publications/commands.d.ts +2 -0
  71. package/dist/publications/commands.js +84 -5
  72. package/dist/publications/commands.js.map +1 -1
  73. package/dist/repositories/client.d.ts +6 -0
  74. package/dist/repositories/commands.d.ts +3 -1
  75. package/dist/repositories/commands.js +22 -8
  76. package/dist/repositories/commands.js.map +1 -1
  77. package/dist/repositories/errors.d.ts +2 -1
  78. package/dist/repositories/errors.js +4 -2
  79. package/dist/repositories/errors.js.map +1 -1
  80. package/dist/repositories/schemas.d.ts +32 -0
  81. package/dist/repositories/schemas.js +4 -0
  82. package/dist/repositories/schemas.js.map +1 -1
  83. package/package.json +4 -4
  84. package/skills/numa-create-application/SKILL.md +26 -13
  85. package/skills/numa-create-application/agents/openai.yaml +2 -2
  86. package/skills/numa-create-application/evals/evals.json +72 -12
  87. package/skills/numa-create-application/references/checklist.md +12 -5
  88. package/skills/numa-create-application/references/numa-cli.md +55 -13
  89. package/skills/numa-create-application/references/promote-workflows.md +63 -24
  90. package/skills/numa-create-application/scripts/inspect-project.mjs +1 -0
package/README.md CHANGED
@@ -194,8 +194,9 @@ numa config \
194
194
  | `numa registry approvals/router/audit` | 需登录 | 两人制发布、Router 健康/对账和追加式审计 |
195
195
  | `numa registry tui` | 需登录 | 打开 namespace、Skills、MCP、审批、Router、审计全屏工作台 |
196
196
  | `numa app capabilities/begin/onboard/publication/jenkins-binding/observer-scope/first-build/...` | 需登录 | 自述运行时能力,创建、接入并按独立 child gate Promote 应用 |
197
- | `numa publication scan/plan/apply/status/events/...` | 公开扫描/需登录写入 | 把 clean Git HEAD 通过受控 bundle 发布到受管仓库 |
197
+ | `numa publication scan/plan/apply/status/events/...` | 公开扫描/需登录写入 | 把 clean Git HEAD 通过受控 bundle 发布到受管仓库;plan 可按原 request ID 只读恢复 |
198
198
  | `numa jenkins job plan/apply/status/events/...` | 需登录 | 创建/校验 Jenkins Multibranch Job,默认不扫描、不构建 |
199
+ | `numa pipeline platform-build trigger/resolve/status/log/stop` | 需登录 | 新应用 v2 immutable snapshot 构建;无任意参数,GitOps 写由平台 SCM CAS 完成 |
199
200
  | `numa app options` | 需登录 | 查询应用归属所需的团队、系统、业务域和环境 ID |
200
201
  | `numa app candidate list/inspect/preview/review/adopt` | 需登录 | 审查应用发现候选并确认收录,服务端执行最终管理员授权和审计 |
201
202
  | `numa media capabilities` | 需登录 | 查询媒体 API、Provider 和版本能力 |
@@ -535,6 +536,29 @@ numa app first-build decide <session-no> --decision defer \
535
536
  --idempotency-key defer-code-index-first-build --yes --json
536
537
  ```
537
538
 
539
+ onboarding v3 在 GitOps structural apply 完成后会于同一 session/task 内自动执行
540
+ server-internal `gitops-platform-writer-bootstrap/v2`,在 Observer 和 first-build 之前锁定并只读写
541
+ base release、exact overlay values、overlay kustomization 三个服务文件。这不是新 CLI
542
+ 或独立 child plan;CLI 只通过原有 `app inspect <session-no> [--watch] [--after-sequence]`
543
+ 读取 `GITOPS_V2_WRITER_BOOTSTRAP` 事件。只有 exact contract/identity/revision/digest 匹配的
544
+ 成功事件才能继续;`PENDING` 只继续等待原 session,`UNKNOWN` 或 drift 均 fail closed。
545
+ 该阶段不调用 v1 `legacy-image-migration`,不枚举、读取或改写 Secret,也不新建
546
+ Application、session、binding 或 target。
547
+
548
+ 若事件进入 `UNKNOWN`,先只读检查原 session;普通 status/inspect 和 first-build 的未知结果恢复
549
+ 都不会重启 bootstrap。只有服务端把同一内部步骤标记为可重入失败时,用户才显式调用既有的
550
+ 通用会话恢复命令;服务端据此恢复原 run/key,不创建新的 bootstrap 请求或业务身份:
551
+
552
+ ```bash
553
+ numa app inspect <session-no> --after-sequence <last-sequence> --json
554
+ numa app retry <session-no> --json
555
+ numa app inspect <session-no> --watch --after-sequence <last-sequence> --json
556
+ ```
557
+
558
+ `app retry` 不接受 bootstrap、SCM、path、Secret、binding、target 或 first-build 参数;调用后仍须等到
559
+ 同一 `GITOPS_V2_WRITER_BOOTSTRAP` 的 exact 成功证据。若状态仍为 `UNKNOWN` 或出现 drift,继续阻断
560
+ Observer/first-build,禁止重放 first-build POST、手工修改 SCM 或改用新 session/identity。
561
+
538
562
  `app publication attach --json` 只返回 session/plan 标识、manifest 计数和 tree digest 摘要,不返回
539
563
  完整 session draft/plan、manifest path、本地绝对路径或文件内容。若写响应在网关处变成结果未知,
540
564
  CLI 会以原 `sessionNo/clientRequestId` 有界轮询固定的 `GET publication-plan/resolve`;只有 200
@@ -543,6 +567,19 @@ CLI 会以原 `sessionNo/clientRequestId` 有界轮询固定的 `GET publication
543
567
  `app publication resolve` 重查;仍无确定结果时返回 `onboarding_publication_result_unknown`,不自动
544
568
  重放 attach。
545
569
 
570
+ 独立 `STANDALONE` Publication(不属于 onboarding parent)在 plan POST 的 500/504、连接中断或无效
571
+ 2xx 后会自动用原 `clientRequestId` 有界调用同一只读 resolve API;POST 始终只发一次。恢复结果必须仍是
572
+ `STANDALONE` 且 `repositoryKey` 与请求一致,否则 fail closed:
573
+
574
+ ```bash
575
+ numa publication plan create --source ./project --repository-key <repository-key> \
576
+ --commit-message 'feat: publish source' --client-request-id <stable-id> \
577
+ --idempotency-key <stable-id> --json
578
+ numa publication plan resolve --client-request-id <stable-id> --json
579
+ ```
580
+
581
+ `PUBLICATION_PLAN_NOT_RESOLVED` 的 retryable 404 只表示可稍后继续 GET;不得重放 plan create。
582
+
546
583
  v3 structural spec 只允许 MCI_FLUX_V1 结构字段,拒绝 image、仓库/分支/namespace/path、raw
547
584
  Kubernetes Secret 和明文敏感环境变量。Job Control Plane 默认 `scanMode=NONE`;`INDEX_ONLY`
548
585
  也必须通过服务端 NoTrigger 配置验证,不能等同于 build。
@@ -580,14 +617,20 @@ Application Onboarding 契约定义。
580
617
  SCM 目录和连接诊断使用同一身份。仓库查询支持服务端 search/cursor 分页,返回稳定的
581
618
  `externalId`、`defaultBranch`、`headRevision`;向导选中仓库后会把 `headRevision` 固定为
582
619
  `expectedHead`。HTTP 429 会保留服务端 `Retry-After` 诊断,Provider 未安装(501)时 TUI
583
- 明确退回手工填写稳定标识。Codeup 安全摘要会额外显示 HTTPS clone credential readiness
584
- `100755` 文件时只有 `codeup_clone_credential_configured=true` 才能继续 Publication:
620
+ 明确退回手工填写稳定标识。Codeup 安全摘要分别显示 OpenAPI Personal AT readiness
621
+ `codeup_access_token_configured` HTTPS clone credential readiness
622
+ `codeup_clone_credential_configured`,但只有
623
+ `codeup_credential_readiness_available=true` 时才解释这两个布尔值。availability 为 `false` 或字段
624
+ 缺失时,即使旧响应显式返回 configured=`false`,也保持 `unknown`;只有 availability=`true` 且
625
+ configured=`false` 才是 `missing`。不能把 `capabilities.executable_mode` 当作 credential 证据。
626
+ 含 `100755` 文件时只有 availability 与 clone configured 同时为 `true` 才能继续 Publication:
585
627
 
586
628
  ```bash
587
629
  numa app scm list --provider codeup --json
588
630
  numa app scm inspect codeup-main --repositories --search order --limit 50 --json
589
631
  numa app scm inspect codeup-main --repositories --cursor '<next-cursor>' --limit 50 --json
590
632
  numa app scm probe codeup-main --json
633
+ numa repository connection list --json
591
634
  ```
592
635
 
593
636
  仓库生命周期也可以脱离应用入驻流程单独使用。先查询 Provider 返回的稳定 namespace
@@ -644,9 +687,9 @@ numa gitops service plan \
644
687
  --spec gitops-service.json \
645
688
  --json
646
689
 
647
- # 只用受管引用规划 legacy Jenkins image writer 迁移;不接受 YAML/path/image/secret
690
+ # 仅旧 v1 应用使用:只用受管引用规划 legacy Jenkins image writer 迁移;不接受 YAML/path/image/secret
648
691
  numa gitops service legacy-image-migration plan \
649
- --application code-index \
692
+ --application legacy-order-service \
650
693
  --target prd \
651
694
  --profile MCI_FLUX_V1 \
652
695
  --json
@@ -654,7 +697,7 @@ numa gitops service legacy-image-migration plan \
654
697
  # 使用 plan 原样返回的 number/hash;结果不明时必须复用同一 Idempotency-Key
655
698
  numa gitops service apply gplan_01 \
656
699
  --plan-hash <64-hex> \
657
- --idempotency-key code-index-prd-image-migration-apply-1 \
700
+ --idempotency-key legacy-order-prd-image-migration-apply-1 \
658
701
  --yes --json
659
702
 
660
703
  # status/events 同时接受 stable runNo 和数字 ID
@@ -680,7 +723,12 @@ spec 不得包含整个 `image`,也不得包含应用、环境、namespace、r
680
723
  `GITOPS_IMAGE_REPOSITORY_UNRESOLVED` 阻断,不会猜测镜像地址。返回的 `canonicalSpecHash`、
681
724
  `planHash`、`baseRevision` 和 `sideEffects` 是后续审阅的锁定证据。
682
725
 
683
- `legacy-image-migration plan` 严格只提交 `applicationCode/targetKey/profileCode/profileVersion/serviceId`。
726
+ `legacy-image-migration plan` 只适用于明确保留 legacy/v1 writer 的旧应用,严格只提交
727
+ `applicationCode/targetKey/profileCode/profileVersion/serviceId`。新应用 v2 不调用该接口;它在
728
+ onboarding v3 GitOps structural apply 之后由服务端内部自动执行
729
+ `gitops-platform-writer-bootstrap/v2`,且只读写该服务的 base release、exact overlay values 和
730
+ overlay kustomization 三个受管文件。CLI 无 bootstrap 命令,只从原 session events 读取
731
+ `GITOPS_V2_WRITER_BOOTSTRAP`;不枚举或修改同一 overlay 的 sibling Secret。
684
732
  路径、image pointers 和文件变更由服务端派生;CLI 只输出 `READY/BLOCKED`、plan number/hash、
685
733
  changed file count、blocker/recovery、warnings 和 side effects,不输出 path/diff/pointer。`READY` 后复用
686
734
  `gitops service apply`。该 plan API 没有 Idempotency-Key 或 resolve-by-key,因此响应不明时必须
@@ -691,49 +739,115 @@ fail closed,不自动重放;只有获得 plan number 后才可用 status/eve
691
739
  `REVIEWED_CHANGE` 会进入 `WAITING_REVIEW` 并返回审查地址。apply 请求只包含 `planNumber`、
692
740
  `planHash` 和 `Idempotency-Key`,不接受任意 repository/path/image 输入。
693
741
 
694
- ## Jenkins GitOps image writer rollout
742
+ ## Jenkins GitOps image writer rollout v1
695
743
 
696
- Jenkins rollout 管理位于 `numa jenkins gitops-rollout`。读取需 Jenkins reader;plan/apply/readiness
697
- 只允许 `mci-devops-platform-ops-admin`。所有写入都要求稳定 `Idempotency-Key` 和 `--yes`,
698
- 且使用 backend `jenkins-gitops-rollout/v1` 的 strict DTO
744
+ Jenkins rollout 管理位于 `numa jenkins gitops-rollout`。读取需 Jenkins reader;
745
+ plan/apply/readiness 只允许 `mci-devops-platform-ops-admin`。所有写入都要求稳定
746
+ `Idempotency-Key` 和 `--yes`,且保持 backend `jenkins-gitops-rollout/v1` 的 strict DTO 和行为。
747
+ 该命令族不包含 automatic configure;新应用不应通过它隐式升级。
699
748
 
700
749
  ```bash
701
- # 规划单应用启用 platform writer;contract 固定为 gitops-image-delivery/v1
750
+ # v1 contract 固定为 gitops-image-delivery/v1
702
751
  numa jenkins gitops-rollout plan enable-platform \
703
- --application code-index --instance numa \
752
+ --application legacy-order-service --instance numa \
704
753
  --shared-library-revision 230e0f00444dfd1874e1ab3e624381341a0c31c5 \
705
- --idempotency-key code-index-rollout-plan-1 --yes --json
754
+ --idempotency-key legacy-order-v1-rollout-plan-1 --yes --json
706
755
 
707
756
  numa jenkins gitops-rollout plan get <plan-no> --json
708
757
 
709
758
  # apply 只持久化 fail-closed rollout gate,不写 Jenkins 或触发 build
710
759
  numa jenkins gitops-rollout apply \
711
760
  --plan-no <plan-no> --plan-hash <64hex> \
712
- --idempotency-key code-index-rollout-apply-1 --yes --json
761
+ --idempotency-key legacy-order-v1-rollout-apply-1 --yes --json
713
762
 
714
- numa jenkins gitops-rollout list --application code-index --instance numa --json
763
+ numa jenkins gitops-rollout list --application legacy-order-service --instance numa --json
715
764
  numa jenkins gitops-rollout get <rollout-no> --json
716
765
 
717
- # 先在 Jenkins trusted 管理面完成返回的 exact operator actions,再做人工证明
718
- numa jenkins gitops-rollout readiness <rollout-no> \
719
- --desired-mode platform --expected-version <version> \
766
+ # v1 readiness 是人工证明,不是 live-read
767
+ numa jenkins gitops-rollout readiness <legacy-rollout-no> \
768
+ --desired-mode legacy --expected-version <version> \
720
769
  --operator-action-reference CHG-20260824-001 \
721
- --idempotency-key code-index-readiness-1 --yes --json
770
+ --idempotency-key legacy-order-readiness-1 --yes --json
722
771
  ```
723
772
 
724
- `readiness` 固定是 `MANUAL_OPERATOR_ATTESTED`,不是 Jenkins live-read。CLI 从当前 rollout 回传
725
- exact public tuple 和固定 credential reference names,不接受 secret value、raw XML、env map
726
- credential value。响应不明时,apply 只有在 list 中发现 exact application/instance/plan mode
727
- 且 durable version 增长才恢复;readiness 只有 GET 同 rollout 显示 exact reference/mode/status/identity
773
+ `readiness` 固定是 `MANUAL_OPERATOR_ATTESTED`,不是 Jenkins live-read。CLI 从当前
774
+ rollout 回传 exact public tuple 和固定 credential reference names,不接受 secret value、raw XML、env map
775
+ credential value。响应不明时,apply 只有在 list 中发现 exact application/instance/plan mode 且 durable
776
+ version 增长才恢复;readiness 只有 GET 同 rollout 显示 exact reference/mode/status/identity
728
777
  且 version 增长才恢复。其余情况均返回 result unknown,不自动重放。
729
778
 
779
+ ## Platform build rollout v2
780
+
781
+ 新应用的 Jenkins 配置门禁位于独立 `numa jenkins platform-build-rollout`,与 v1
782
+ `gitops-rollout` 的 API、DTO、表和状态机分离。v2 只支持 `ENABLE_PLATFORM`/
783
+ `DISABLE_PLATFORM`,没有 manual readiness,不要求 Jenkins 持有 Flux Git credential。
784
+
785
+ ```bash
786
+ numa jenkins platform-build-rollout plan enable-platform \
787
+ --application code-index --instance numa \
788
+ --shared-library-revision <approved-v2-shared-library-revision> \
789
+ --idempotency-key code-index-v2-rollout-plan-1 --yes --json
790
+
791
+ numa jenkins platform-build-rollout plan get <plan-no> --json
792
+ numa jenkins platform-build-rollout apply \
793
+ --plan-no <plan-no> --plan-hash <64hex> \
794
+ --idempotency-key code-index-v2-rollout-apply-1 --yes --json
795
+ numa jenkins platform-build-rollout list --application code-index --instance numa --json
796
+ numa jenkins platform-build-rollout get <rollout-no> --json
797
+ numa jenkins platform-build-rollout configure <rollout-no> \
798
+ --expected-version <version> \
799
+ --idempotency-key code-index-v2-auto-configure-1 --yes --json
800
+ ```
801
+
802
+ `configure` 的 strict body 只有 `expectedVersion`,不接收 public tuple、env、XML、Groovy、
803
+ token、credential ID/value 或 operator attestation。服务端只有在返回 exact
804
+ `READY/JENKINS_API_VERIFIED/VERIFIED`、writer credential readiness、observed shared-library/source
805
+ revision 以及 Jenkinsfile/profile/configuration digest 时才放行。响应不明时 CLI 只 GET
806
+ 同一 rollout,不重放 POST。
807
+
808
+ automatic configure 之前,Project Publication CAS 必须发布 exact
809
+ `@Library('mciShareLib@<approvedSharedLibraryRevision>') _`、唯一 `platformBuildEntry()` 和 strict
810
+ `.mci/platform-build.yaml`。生产 snapshot 执行服务端固定 wrapper,不加载应用 Jenkinsfile;
811
+ Jenkins 只 checkout 锁定源码并构建/push 镜像,Flux SCM CAS 由 DevOps 服务端执行。
812
+
813
+ ## Platform build v2
814
+
815
+ 新应用使用独立 `pipeline platform-build` 表面;旧 `pipeline build/builds/status/log/stop/retry` 的 v1
816
+ 请求和响应保持不变。v2 不接受任意 Jenkins 参数,生产审批依据只保留在平台审计中。镜像构建完成后,
817
+ Jenkins 只提交一次 image intent;DevOps 服务端持有 Flux writer credential,并按锁定 contract/HEAD
818
+ 执行 SCM CAS。运行态完成证据仍来自独立 Observer。
819
+
820
+ ```bash
821
+ numa pipeline platform-build trigger code-index \
822
+ --tier production --branch master \
823
+ --idempotency-key code-index-prd-first-v2 \
824
+ --production-reason '新运维系统上线前首次生产构建与部署' \
825
+ --yes --wait --json
826
+
827
+ # trigger 响应未知时只读恢复,绝不重放 POST
828
+ numa pipeline platform-build resolve \
829
+ --client-request-id code-index-prd-first-v2 --json
830
+ numa pipeline platform-build status <request-id> --watch --json
831
+ numa pipeline platform-build log <request-id> --start 0 --json
832
+
833
+ # stop 同样只发送一次;未知后只读同一个 request
834
+ numa pipeline platform-build stop <request-id> \
835
+ --idempotency-key code-index-prd-stop-v2 \
836
+ --production-reason '停止异常的首次生产构建' \
837
+ --yes --json
838
+ ```
839
+
840
+ 首次 v2 build 的正式入口仍由原 onboarding session 的 first-build action 创建,独立命令用于恢复与
841
+ 运维;不得新建第二 Application、Repository、session、binding 或 target。任何 result unknown 都必须
842
+ 使用原 key/request 只读 resolve/status,不能换 key 重放。
843
+
730
844
  SCM 管理接口位于 `/api/v1/admin/scm-connections`,最终权限始终由网关和后端按 canonical
731
845
  Client Role 校验。metadata JSON 只允许 provider、URL、组织、auth type 以及服务端托管的
732
846
  `aliyunAccountId`/`aliyunAssumeRoleId` 引用;Numa 不接收云 AK。CODEUP 必须引用
733
847
  `aliyunAccountId`,没有 access token 时直接使用服务端 Aliyun identity,不写入空 credential。
734
848
  Codeup Personal AT 只用于 OpenAPI,不能当作 HTTPS Git 密码;包含 `100755` 文件的 Publication
735
849
  另需 Codeup 的 HTTPS clone username/password。GitHub App/PAT 和这三项 Codeup credential 均为
736
- WRITE_ONLY,只能从 owner-only 文件或重定向 stdin 读取,禁止放在 argv、环境变量和 metadata
850
+ WRITE_ONLY,只能从隐藏终端提示、owner-only 文件或重定向 stdin 读取,禁止放在 argv、环境变量和 metadata
737
851
  JSON 中,且不会出现在输出或错误中。安全响应只返回 credential readiness boolean:
738
852
 
739
853
  ```bash
@@ -741,7 +855,7 @@ numa app scm create --input codeup.json --json
741
855
  chmod 600 github-app.private.json
742
856
  numa app scm create --input github.json --credential-file github-app.private.json --json
743
857
  numa app scm rotate github-main --revision 4 --credential-stdin --yes --json < github-pat.private.json
744
- numa app scm rotate codeup-main --revision 5 --credential-file codeup.private.json --yes --json
858
+ numa app scm rotate codeup-main --revision 5 --credential-prompt --yes --json
745
859
  numa app scm disable old-codeup --revision 7 --yes --json
746
860
  ```
747
861
 
@@ -155,6 +155,7 @@ export declare class OnboardingClient implements OnboardingApi {
155
155
  health: string;
156
156
  capabilities: Record<string, boolean>;
157
157
  credentialConfigured?: boolean | undefined;
158
+ codeupCredentialReadinessAvailable?: boolean | undefined;
158
159
  codeupAccessTokenConfigured?: boolean | undefined;
159
160
  codeupCloneCredentialConfigured?: boolean | undefined;
160
161
  lastCheckedAt?: string | null | undefined;
@@ -48,6 +48,7 @@ export interface PublicationAttachmentRecoveryOptions {
48
48
  delaysMs?: number[];
49
49
  sleep?: (milliseconds: number) => Promise<void>;
50
50
  }
51
+ export declare function canAttachPublicationPlan(inspection: OnboardingInspection): boolean;
51
52
  export type OnboardingRecoveryAction = "cancel" | "retry" | "resume" | "reassess";
52
53
  export type ApplicationCatalogOptionType = "team" | "system" | "business-domain" | "environment";
53
54
  export declare function publicOnboardingEvent(event: OnboardingEvent): OnboardingEvent;
@@ -56,6 +57,7 @@ export declare function parseOnboardingMode(value: string): OnboardingMode;
56
57
  export declare function parseApplicationCatalogOptionType(value: string): ApplicationCatalogOptionType;
57
58
  export declare function resolveScmConnection(client: Pick<OnboardingApi, "listScmConnections">, code: string): Promise<ScmConnection>;
58
59
  export declare function scmCredentialUpdate(version: number, authType: ScmAuthType, credential: ScmCredentialBundle): ScmCredentialUpdate;
60
+ export declare function scmConnectionLine(connection: ScmConnection): string;
59
61
  export declare function readOnboardingSpec(path: string): Promise<OnboardingSpec>;
60
62
  export declare function executeOnboardingSpec(client: OnboardingApi, spec: OnboardingSpec, options?: {
61
63
  mode?: OnboardingMode;
@@ -24,6 +24,20 @@ const STATE_SUMMARIES = {
24
24
  FAILED: "服务端执行失败;请依据事件中的 errorCode 和 recoveryAction 恢复",
25
25
  CANCELLED: "服务端已取消尚未开始的步骤;既有外部资源未回滚"
26
26
  };
27
+ export function canAttachPublicationPlan(inspection) {
28
+ const { session } = inspection;
29
+ if (session.state !== "WAITING_EXTERNAL" || !session.planHash)
30
+ return false;
31
+ if (session.currentStep === "ATTACH_PUBLICATION_PLAN")
32
+ return true;
33
+ if (session.currentStep !== "EXECUTION")
34
+ return false;
35
+ const latest = inspection.events.at(-1);
36
+ return latest?.eventType === "WAITING_EXTERNAL"
37
+ && latest.stepCode === "ATTACH_PUBLICATION_PLAN"
38
+ && latest.stepState === "WAITING"
39
+ && latest.output?.planHash === session.planHash;
40
+ }
27
41
  function addExamples(command, examples, notes = []) {
28
42
  const noteSection = notes.length
29
43
  ? `\n说明:\n${notes.map((item) => ` - ${item}`).join("\n")}\n`
@@ -187,14 +201,17 @@ export function scmCredentialUpdate(version, authType, credential) {
187
201
  return { version, githubPat: credential };
188
202
  return { version, codeup: credential };
189
203
  }
190
- function scmConnectionLine(connection) {
204
+ function readiness(value, available) {
205
+ if (available !== true || value == null)
206
+ return "unknown";
207
+ return value ? "ready" : "missing";
208
+ }
209
+ export function scmConnectionLine(connection) {
191
210
  const enabled = Object.entries(connection.capabilities).filter(([, value]) => value).map(([key]) => key);
192
- const codeupCloneReadiness = connection.provider === "CODEUP"
193
- ? `\tcodeup-clone:${connection.codeupCloneCredentialConfigured == null
194
- ? "unknown"
195
- : connection.codeupCloneCredentialConfigured ? "ready" : "missing"}`
211
+ const codeupReadiness = connection.provider === "CODEUP"
212
+ ? `\tcodeup-at:${readiness(connection.codeupAccessTokenConfigured, connection.codeupCredentialReadinessAvailable)}\tcodeup-clone:${readiness(connection.codeupCloneCredentialConfigured, connection.codeupCredentialReadinessAvailable)}`
196
213
  : "";
197
- return `${connection.code}\t${connection.provider}\t${connection.health}\t${connection.organizationKey}\t${enabled.join(",") || "no-capabilities"}${codeupCloneReadiness}`;
214
+ return `${connection.code}\t${connection.provider}\t${connection.health}\t${connection.organizationKey}\t${enabled.join(",") || "no-capabilities"}${codeupReadiness}`;
198
215
  }
199
216
  function repositoryLine(repository) {
200
217
  return `${repository.externalId}\t${repository.fullName ?? repository.name}\t${repository.defaultBranch ?? "-"}\t${repository.headRevision ?? "-"}${repository.archived ? "\tARCHIVED" : ""}`;
@@ -649,10 +666,10 @@ export function registerApplicationOnboardingCommands(program, dependencies = {}
649
666
  安全边界:
650
667
  普通诊断只显示 code、provider、organization、health 和 capability。
651
668
  live probe 与连接变更由服务端校验平台管理员权限和乐观锁 version。
652
- provider credential 只能来自权限受限文件或重定向 stdin;禁止通过 argv、环境变量或普通 JSON 输出传递。
669
+ provider credential 只能来自隐藏终端提示、权限受限文件或重定向 stdin;禁止通过 argv、环境变量或普通 JSON 输出传递。
653
670
  Codeup 云 AK/Role 只能引用已有 aliyunAccountId/aliyunAssumeRoleId,Numa 不读取或保存云 AK。
654
671
  Codeup accessToken 只用于 OpenAPI;含 100755 文件的 Publication 另需 HTTPS cloneUsername/clonePassword。
655
- 三者只从 chmod 600 文件或重定向 stdin 写入;未提供时使用服务端 Aliyun identity,不提交空 credential bundle。
672
+ 三者只从隐藏终端提示、chmod 600 文件或重定向 stdin 写入;未提供时使用服务端 Aliyun identity,不提交空 credential bundle。
656
673
  `);
657
674
  addExamples(scm.command("list")
658
675
  .description("列出当前用户可见的安全 SCM 连接摘要")
@@ -793,6 +810,7 @@ export function registerApplicationOnboardingCommands(program, dependencies = {}
793
810
  .requiredOption("--input <file>", "无 secret 的连接 metadata JSON")
794
811
  .option("--credential-file <file>", "仅 owner 可读(chmod 600)的 provider credential JSON")
795
812
  .option("--credential-stdin", "从重定向 stdin 读取 provider credential JSON")
813
+ .option("--credential-prompt", "在交互终端隐藏输入 provider credential")
796
814
  .action(async (options, command) => {
797
815
  const onboarding = client();
798
816
  const input = await readScmAdminConnectionInput(options.input);
@@ -808,7 +826,7 @@ export function registerApplicationOnboardingCommands(program, dependencies = {}
808
826
  await output(command, { ok: true, connection }, `${connection.code}\t${connection.status}\tversion:${connection.version}\tcredential:${connection.credentialConfigured ? "configured" : "missing"}\tcodeup-at:${connection.codeupAccessTokenConfigured ? "ready" : "missing"}\tcodeup-clone:${connection.codeupCloneCredentialConfigured ? "ready" : "missing"}`);
809
827
  }), [
810
828
  "numa app scm create --input github.json --credential-file github-app.private.json --json",
811
- "numa app scm create --input codeup.json --json"
829
+ "numa app scm create --input codeup.json --credential-prompt --json"
812
830
  ]);
813
831
  addExamples(scm.command("update <code>")
814
832
  .description("平台管理员按 version 更新连接 metadata;身份字段变更可能使已有 plan 失效")
@@ -833,11 +851,12 @@ export function registerApplicationOnboardingCommands(program, dependencies = {}
833
851
  .requiredOption("--revision <number>", "当前乐观锁 version")
834
852
  .option("--credential-file <file>", "仅 owner 可读(chmod 600)的 provider credential JSON")
835
853
  .option("--credential-stdin", "从重定向 stdin 读取 provider credential JSON")
854
+ .option("--credential-prompt", "在交互终端隐藏输入 provider credential")
836
855
  .option("--yes", "确认凭据轮换并使旧 plan 失效")
837
856
  .action(async (code, options, command) => {
838
857
  requireConfirmation(options.yes, "SCM credential rotation");
839
- if (!options.credentialFile && !options.credentialStdin) {
840
- throw new OnboardingError("SCM credential rotation requires --credential-file or --credential-stdin.", "onboarding_scm_credential_source_invalid");
858
+ if (!options.credentialFile && !options.credentialStdin && !options.credentialPrompt) {
859
+ throw new OnboardingError("SCM credential rotation requires --credential-prompt, --credential-file, or --credential-stdin.", "onboarding_scm_credential_source_invalid");
841
860
  }
842
861
  const onboarding = client();
843
862
  const version = revision(options.revision);
@@ -851,7 +870,7 @@ export function registerApplicationOnboardingCommands(program, dependencies = {}
851
870
  await output(command, { ok: true, connection }, `${connection.code}\tcredential:configured\tversion:${connection.version}`);
852
871
  }), [
853
872
  "numa app scm rotate github-main --revision 4 --credential-file github-app.private.json --yes --json",
854
- "numa app scm rotate codeup-main --revision 2 --credential-stdin --yes --json < codeup.private.json"
873
+ "numa app scm rotate codeup-main --revision 2 --credential-prompt --yes --json"
855
874
  ]);
856
875
  addExamples(scm.command("disable <code>")
857
876
  .description("平台管理员软禁用连接;服务端会阻止仍被非终态会话使用的连接")
@@ -961,9 +980,15 @@ export function registerApplicationOnboardingCommands(program, dependencies = {}
961
980
  throw new OnboardingError("idempotency-key must exactly match client-request-id for publication attachment.", "onboarding_input_invalid");
962
981
  }
963
982
  const onboarding = client();
964
- const session = await onboarding.getSession(sessionNo);
965
- if (session.state !== "WAITING_EXTERNAL" || session.currentStep !== "ATTACH_PUBLICATION_PLAN") {
966
- throw new OnboardingError("Publication attachment requires the same parent session to be WAITING_EXTERNAL at ATTACH_PUBLICATION_PLAN.", "onboarding_publication_session_invalid", undefined, undefined, false, { sessionNo: session.sessionNo, state: session.state, currentStep: session.currentStep ?? null });
983
+ const inspection = await inspectOnboardingSession(onboarding, sessionNo);
984
+ const session = inspection.session;
985
+ if (!canAttachPublicationPlan(inspection)) {
986
+ throw new OnboardingError("Publication attachment requires the same parent session to have durable WAITING_EXTERNAL evidence for ATTACH_PUBLICATION_PLAN.", "onboarding_publication_session_invalid", undefined, undefined, false, {
987
+ sessionNo: session.sessionNo,
988
+ state: session.state,
989
+ currentStep: session.currentStep ?? null,
990
+ lastSequence: inspection.lastSequence
991
+ });
967
992
  }
968
993
  const source = await scanPublication(options.source);
969
994
  const input = parsedActionInput(OnboardingPublicationPlanActionRequestSchema, {
@@ -1180,7 +1205,7 @@ export function registerApplicationOnboardingCommands(program, dependencies = {}
1180
1205
  "numa app tui --session 8d15ee89-43f0-4378-a7aa-51f09c5b42fc"
1181
1206
  ]);
1182
1207
  addExamples(app.command("retry <session-no>")
1183
- .description("让服务端重试可重入的失败步骤")
1208
+ .description("显式请求服务端恢复同一会话中可重入的失败步骤")
1184
1209
  .action(async (sessionNo, _options, command) => {
1185
1210
  const inspection = await executeOnboardingAction(client(), sessionNo, "retry");
1186
1211
  await output(command, { ok: true, ...inspection }, executionText(inspection.session, inspection.events));