@clawos-dev/clawd 0.2.581 → 0.2.582

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.
@@ -21706,6 +21706,12 @@ var DeployRunSchema = external_exports.object({
21706
21706
  /** run 刚入队时可能一条都还没有 */
21707
21707
  jobs: external_exports.array(DeployJobSchema)
21708
21708
  }).strict();
21709
+ var DeployUnpublishRunSchema = external_exports.object({
21710
+ runId: external_exports.number(),
21711
+ status: DeployRunSchema.shape.status,
21712
+ conclusion: external_exports.enum(["success", "failure"]).optional(),
21713
+ htmlUrl: external_exports.string().min(1)
21714
+ }).strict();
21709
21715
  var DeployStateSchema = external_exports.object({
21710
21716
  personaId: external_exports.string().min(1),
21711
21717
  /** 本机 GitHub 身份(机器级,githubAuth:* 的同一份;上云页顶上那张卡用)。老 daemon 不给(可选只为兼容),UI 缺席时按未知处理 */
@@ -21722,12 +21728,23 @@ var DeployStateSchema = external_exports.object({
21722
21728
  * **缺席是常态**:没推过、没授权 GitHub、GitHub 查不到、仓库还没有 run,都不该让整页打不开——同 `secrets` 的处理。
21723
21729
  */
21724
21730
  run: DeployRunSchema.optional(),
21731
+ /**
21732
+ * 现在在不在线上 = gateway 的正式登记表里有没有它(`/healthz`,跟 persona 列表里云卡片同一个来源)。
21733
+ * **缺席 = 查不到**(没登录飞书 / 连不上云上服务),不是「不在」——页面据此不给下线按钮,也不撤「线上」标。
21734
+ * 不看 `releases`:发布过不等于还在线上(下线过、或者被人用脚本撤了)
21735
+ */
21736
+ online: external_exports.boolean().optional(),
21737
+ /** 最新那次下线流水线。没下线过 / 没仓库 / 没 GitHub 身份 / 查不到都缺席 */
21738
+ unpublish: DeployUnpublishRunSchema.optional(),
21725
21739
  /**
21726
21740
  * `.clawd/deploy-scan.json` 读不出来(坏 JSON / 不合形状)时的人话。有它 `scans` 为空。
21727
21741
  * 页面据此给一条能走的路(重新扫描会重建这个文件),而不是白屏。
21728
21742
  */
21729
21743
  historyError: external_exports.string().optional()
21730
21744
  }).strict();
21745
+ var DeployUnpublishResultSchema = external_exports.object({
21746
+ before: external_exports.number().nullable()
21747
+ }).strict();
21731
21748
  var DeployReleaseResultSchema = external_exports.object({
21732
21749
  sha: external_exports.string().min(1),
21733
21750
  actionsUrl: external_exports.string().min(1)
@@ -24275,6 +24292,10 @@ var METHOD_DOCS = {
24275
24292
  summary: "\u539F\u6837\u91CD\u8DD1\u6700\u65B0\u90A3\u6761\u6D41\u6C34\u7EBF\u91CC\u5931\u8D25\u7684\u67D0\u4E00\u6BB5\uFF08GitHub REST rerun job\uFF09\u3002**\u540C\u4E00\u4E2A commit\u3001\u540C\u4E00\u4EFD\u8F93\u5165**\uFF0C\u4E0D\u4EA7\u751F\u65B0\u63D0\u4EA4\u2014\u2014\u6539\u4E86 persona \u5185\u5BB9\u8981 deploy:scan \u518D deploy:release\u3002jobId \u5FC5\u987B\u5C5E\u4E8E\u6700\u65B0\u90A3\u6761 run\uFF1Bowner-only",
24276
24293
  args: DeployRerunJobArgsSchema
24277
24294
  },
24295
+ "deploy:unpublish": {
24296
+ summary: "\u4E0B\u7EBF\uFF1A\u89E6\u53D1 persona \u4ED3\u5E93\u91CC\u7684 clawd-unpublish.yml\uFF08workflow_dispatch\uFF09\uFF0C\u7531\u6D41\u6C34\u7EBF\u5728 gateway \u64A4\u6389\u6B63\u5F0F\u767B\u8BB0\u2014\u2014\u522B\u4EBA\u770B\u4E0D\u5230\u3001\u7528\u4E0D\u4E86\uFF1B\u4ED3\u5E93 / \u955C\u50CF / \u672C\u673A\u4E0A\u4E91\u8BB0\u5F55\u90FD\u4E0D\u52A8\uFF0C\u91CD\u65B0\u53D1\u5E03\u5C31\u56DE\u6765\u3002\u6700\u540E\u4E00\u6B21\u53D1\u5E03\u7684 commit \u91CC\u6CA1\u6709\u8FD9\u4EFD workflow\uFF08\u65E7\u6A21\u677F\uFF09\u65F6\u62D2\uFF0C\u8981\u5148\u91CD\u65B0\u53D1\u5E03\u4E00\u6B21\u3002\u56DE { before }\uFF1A\u89E6\u53D1\u4E4B\u524D\u73B0\u67E5\u7684\u6700\u65B0\u4E0B\u7EBF run id\uFF08\u6CA1\u4E0B\u7EBF\u8FC7 = null\uFF09\uFF0C\u9875\u9762\u62FF\u5B83\u8BA4\u65B0 run\uFF1Bowner-only\u3001human-only",
24297
+ args: DeployPersonaArgsSchema
24298
+ },
24278
24299
  // ---- githubAuth:*(本机 GitHub 身份,机器级;spec 2026-09-15-github-auth-unify-design §4.3)----
24279
24300
  "githubAuth:get": {
24280
24301
  summary: "\u672C\u673A\u7684 GitHub \u8EAB\u4EFD\uFF1Aauthorized\uFF08@login\uFF0C\u6765\u81EA git \u51ED\u636E\u5B58\u50A8 / \u672C\u673A\u51ED\u636E\u8868\uFF09/ missing / invalid\uFF08GitHub \u4E0D\u8BA4\uFF09/ pending\uFF08\u7B49 GitHub \u8DF3\u56DE\u4E2D\u5FC3\uFF09\u3002\u8BBE\u7F6E\u9875\u300C\u8D26\u53F7\u300D\u3001\u4E0A\u4E91\u9875\u9876\u4E0A\u90A3\u5F20\u5361\u3001PR \u6536\u4EF6\u7BB1\u90FD\u770B\u5B83\uFF1Bpending \u671F\u9875\u9762\u6BCF 2 \u79D2\u8F6E\u8BE2\uFF0Cdaemon \u987A\u624B\u5411\u4E2D\u5FC3\u9886 token\u3002\u65E0\u53C2\u6570",
@@ -24850,6 +24871,10 @@ var PackageFindingSchema = EnvPackageSchema.omit({ localVersion: true, artifact:
24850
24871
  // src/deploy/templates.ts
24851
24872
  var CLAWOS_REPO = "ottin4ttc/clawos";
24852
24873
  var CLAWOS_REF = "release";
24874
+ var DEPLOY_WORKFLOW_FILE = "clawd-deploy.yml";
24875
+ var WORKFLOW_PATH = `.github/workflows/${DEPLOY_WORKFLOW_FILE}`;
24876
+ var UNPUBLISH_WORKFLOW_FILE = "clawd-unpublish.yml";
24877
+ var UNPUBLISH_WORKFLOW_PATH = `.github/workflows/${UNPUBLISH_WORKFLOW_FILE}`;
24853
24878
  var ACR_REGISTRY = "registry.cn-hangzhou.aliyuncs.com";
24854
24879
  var ACR_VPC_REGISTRY = "registry-vpc.cn-hangzhou.aliyuncs.com";
24855
24880
  var ACR_NAMESPACE = "ttc-prod";
@@ -24903,6 +24928,7 @@ var ACR_LOGIN_STEPS = [
24903
24928
  ];
24904
24929
  var IMAGE_LATEST = '"$REGISTRY/$ACR_NAMESPACE/${{ steps.plan.outputs.persona_id }}:latest"';
24905
24930
  var RESOLVE_LINE = ` IMAGE_REF=$(node clawos/cloud-host/pipeline/acr.mjs resolve --image ${IMAGE_LATEST} --vpc-registry "$VPC_REGISTRY")`;
24931
+ var CONCURRENCY_GROUP = " group: persona-cloud-${{ github.repository }}";
24906
24932
  var L = [
24907
24933
  "name: clawd deploy",
24908
24934
  "",
@@ -24923,7 +24949,7 @@ var L = [
24923
24949
  "",
24924
24950
  "concurrency:",
24925
24951
  " # \u540C\u4E00\u4E2A\u4ED3\u5E93\u7684\u53D1\u5E03\u6392\u961F\u8DD1\uFF0C\u4E0D\u53D6\u6D88\uFF1A\u4E0A\u4E00\u6B21\u53EF\u80FD\u5DF2\u7ECF\u5728\u767B\u8BB0\u6B63\u5F0F\u4E86",
24926
- " group: persona-cloud-${{ github.repository }}",
24952
+ CONCURRENCY_GROUP,
24927
24953
  "",
24928
24954
  "env:",
24929
24955
  ` REGISTRY: ${ACR_REGISTRY}`,
@@ -25059,6 +25085,44 @@ var L = [
25059
25085
  ""
25060
25086
  ];
25061
25087
  var WORKFLOW_TEMPLATE = L.join("\n");
25088
+ var U = [
25089
+ "name: clawd unpublish",
25090
+ "",
25091
+ "# \u7531 clawd \u751F\u6210\u3001\u7531 clawd \u7EF4\u62A4\uFF1A\u6BCF\u6B21\u300C\u53D1\u5E03\u300D\u90FD\u4F1A\u7528\u5F53\u524D\u6A21\u677F\u91CD\u5199\u8FD9\u4EFD\u6587\u4EF6\u518D\u63D0\u4EA4\uFF0C\u624B\u6539\u4F1A\u88AB\u76D6\u6389\u3002",
25092
+ "# \u4E0B\u7EBF\uFF1A\u5728 gateway \u64A4\u6389\u8FD9\u4E2A persona \u7684\u6B63\u5F0F\u767B\u8BB0\u2014\u2014\u522B\u4EBA\u770B\u4E0D\u5230\u3001\u7528\u4E0D\u4E86\uFF1B\u4ED3\u5E93\u548C\u955C\u50CF\u90FD\u4E0D\u52A8\uFF0C\u91CD\u65B0\u53D1\u5E03\u5C31\u56DE\u6765\u3002",
25093
+ "# \u53EA\u7531 clawd \u4E0A\u4E91\u9875\u7684\u300C\u4E0B\u7EBF\u300D\u89E6\u53D1\uFF08workflow_dispatch\uFF09\uFF0C\u63A8\u4EE3\u7801\u4E0D\u8DD1\u3002\u6B63\u5728\u8DD1\u7684\u5BF9\u8BDD\u4E0D\u6253\u65AD\uFF0C\u8DD1\u5230\u7A7A\u95F2\u81EA\u5DF1\u9000\u3002",
25094
+ "#",
25095
+ "# \u8981\u7684\u7EC4\u7EC7\u7EA7 Secrets\uFF1ACLAWOS_READ_TOKEN\uFF08\u8BFB clawos \u4ED3\u5E93\uFF09\u3001CLAWD_GATEWAY_ADMIN_KEY\uFF1B\u7EC4\u7EC7\u7EA7 Variable\uFF1ACLAWD_GATEWAY_URL\u3002",
25096
+ "",
25097
+ "on:",
25098
+ " workflow_dispatch:",
25099
+ "",
25100
+ "concurrency:",
25101
+ " # \u4E0E\u53D1\u5E03\u540C\u4E00\u7EC4\uFF1A\u53D1\u5E03\u5728\u8DD1\u65F6\u70B9\u4E0B\u7EBF\uFF0C\u6392\u5728\u5B83\u540E\u9762\uFF1B\u540E\u70B9\u7684\u9876\u6389\u5148\u70B9\u7684",
25102
+ CONCURRENCY_GROUP,
25103
+ "",
25104
+ "env:",
25105
+ ` CLAWOS_REPO: ${CLAWOS_REPO}`,
25106
+ ` CLAWOS_REF: ${CLAWOS_REF}`,
25107
+ "",
25108
+ "permissions:",
25109
+ " contents: read",
25110
+ "",
25111
+ "jobs:",
25112
+ " unpublish:",
25113
+ " name: unpublish",
25114
+ " runs-on: ubuntu-latest",
25115
+ " timeout-minutes: 10",
25116
+ " steps:",
25117
+ ...CONTRACT_STEPS,
25118
+ " - name: \u64A4\u767B\u8BB0",
25119
+ " env:",
25120
+ " CLAWD_GATEWAY_URL: ${{ vars.CLAWD_GATEWAY_URL }}",
25121
+ " CLAWD_GATEWAY_ADMIN_KEY: ${{ secrets.CLAWD_GATEWAY_ADMIN_KEY }}",
25122
+ " run: node clawos/cloud-host/pipeline/unregister.mjs --persona-id '${{ steps.plan.outputs.persona_id }}'",
25123
+ ""
25124
+ ];
25125
+ var UNPUBLISH_WORKFLOW_TEMPLATE = U.join("\n");
25062
25126
 
25063
25127
  // src/deploy/exec.ts
25064
25128
  var import_node_child_process2 = require("child_process");
package/dist/cli.cjs CHANGED
@@ -6168,7 +6168,7 @@ function cloudEngineOk(tool) {
6168
6168
  function repoSlug(url) {
6169
6169
  return url.trim().replace(/^git@[^:]+:/, "").replace(/^https?:\/\/[^/]+\//, "").replace(/\.git$/, "");
6170
6170
  }
6171
- var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployReleasedSchema, DeployScanFileSchema, DeployRepoSchema, DeploySecretPresenceSchema, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, CLOUD_ENGINES, DeployStateSchema, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployGetArgsSchema, DeployScanArgsSchema, DeployReleaseArgsSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
6171
+ var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployReleasedSchema, DeployScanFileSchema, DeployRepoSchema, DeploySecretPresenceSchema, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, CLOUD_ENGINES, DeployUnpublishRunSchema, DeployStateSchema, DeployUnpublishResultSchema, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployGetArgsSchema, DeployScanArgsSchema, DeployReleaseArgsSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
6172
6172
  var init_deploy_schemas = __esm({
6173
6173
  "../protocol/src/deploy-schemas.ts"() {
6174
6174
  "use strict";
@@ -6277,6 +6277,12 @@ var init_deploy_schemas = __esm({
6277
6277
  jobs: external_exports.array(DeployJobSchema)
6278
6278
  }).strict();
6279
6279
  CLOUD_ENGINES = ["codex", "pi"];
6280
+ DeployUnpublishRunSchema = external_exports.object({
6281
+ runId: external_exports.number(),
6282
+ status: DeployRunSchema.shape.status,
6283
+ conclusion: external_exports.enum(["success", "failure"]).optional(),
6284
+ htmlUrl: external_exports.string().min(1)
6285
+ }).strict();
6280
6286
  DeployStateSchema = external_exports.object({
6281
6287
  personaId: external_exports.string().min(1),
6282
6288
  /** 本机 GitHub 身份(机器级,githubAuth:* 的同一份;上云页顶上那张卡用)。老 daemon 不给(可选只为兼容),UI 缺席时按未知处理 */
@@ -6293,12 +6299,23 @@ var init_deploy_schemas = __esm({
6293
6299
  * **缺席是常态**:没推过、没授权 GitHub、GitHub 查不到、仓库还没有 run,都不该让整页打不开——同 `secrets` 的处理。
6294
6300
  */
6295
6301
  run: DeployRunSchema.optional(),
6302
+ /**
6303
+ * 现在在不在线上 = gateway 的正式登记表里有没有它(`/healthz`,跟 persona 列表里云卡片同一个来源)。
6304
+ * **缺席 = 查不到**(没登录飞书 / 连不上云上服务),不是「不在」——页面据此不给下线按钮,也不撤「线上」标。
6305
+ * 不看 `releases`:发布过不等于还在线上(下线过、或者被人用脚本撤了)
6306
+ */
6307
+ online: external_exports.boolean().optional(),
6308
+ /** 最新那次下线流水线。没下线过 / 没仓库 / 没 GitHub 身份 / 查不到都缺席 */
6309
+ unpublish: DeployUnpublishRunSchema.optional(),
6296
6310
  /**
6297
6311
  * `.clawd/deploy-scan.json` 读不出来(坏 JSON / 不合形状)时的人话。有它 `scans` 为空。
6298
6312
  * 页面据此给一条能走的路(重新扫描会重建这个文件),而不是白屏。
6299
6313
  */
6300
6314
  historyError: external_exports.string().optional()
6301
6315
  }).strict();
6316
+ DeployUnpublishResultSchema = external_exports.object({
6317
+ before: external_exports.number().nullable()
6318
+ }).strict();
6302
6319
  DeployReleaseResultSchema = external_exports.object({
6303
6320
  sha: external_exports.string().min(1),
6304
6321
  actionsUrl: external_exports.string().min(1)
@@ -9139,6 +9156,10 @@ var init_methods = __esm({
9139
9156
  summary: "\u539F\u6837\u91CD\u8DD1\u6700\u65B0\u90A3\u6761\u6D41\u6C34\u7EBF\u91CC\u5931\u8D25\u7684\u67D0\u4E00\u6BB5\uFF08GitHub REST rerun job\uFF09\u3002**\u540C\u4E00\u4E2A commit\u3001\u540C\u4E00\u4EFD\u8F93\u5165**\uFF0C\u4E0D\u4EA7\u751F\u65B0\u63D0\u4EA4\u2014\u2014\u6539\u4E86 persona \u5185\u5BB9\u8981 deploy:scan \u518D deploy:release\u3002jobId \u5FC5\u987B\u5C5E\u4E8E\u6700\u65B0\u90A3\u6761 run\uFF1Bowner-only",
9140
9157
  args: DeployRerunJobArgsSchema
9141
9158
  },
9159
+ "deploy:unpublish": {
9160
+ summary: "\u4E0B\u7EBF\uFF1A\u89E6\u53D1 persona \u4ED3\u5E93\u91CC\u7684 clawd-unpublish.yml\uFF08workflow_dispatch\uFF09\uFF0C\u7531\u6D41\u6C34\u7EBF\u5728 gateway \u64A4\u6389\u6B63\u5F0F\u767B\u8BB0\u2014\u2014\u522B\u4EBA\u770B\u4E0D\u5230\u3001\u7528\u4E0D\u4E86\uFF1B\u4ED3\u5E93 / \u955C\u50CF / \u672C\u673A\u4E0A\u4E91\u8BB0\u5F55\u90FD\u4E0D\u52A8\uFF0C\u91CD\u65B0\u53D1\u5E03\u5C31\u56DE\u6765\u3002\u6700\u540E\u4E00\u6B21\u53D1\u5E03\u7684 commit \u91CC\u6CA1\u6709\u8FD9\u4EFD workflow\uFF08\u65E7\u6A21\u677F\uFF09\u65F6\u62D2\uFF0C\u8981\u5148\u91CD\u65B0\u53D1\u5E03\u4E00\u6B21\u3002\u56DE { before }\uFF1A\u89E6\u53D1\u4E4B\u524D\u73B0\u67E5\u7684\u6700\u65B0\u4E0B\u7EBF run id\uFF08\u6CA1\u4E0B\u7EBF\u8FC7 = null\uFF09\uFF0C\u9875\u9762\u62FF\u5B83\u8BA4\u65B0 run\uFF1Bowner-only\u3001human-only",
9161
+ args: DeployPersonaArgsSchema
9162
+ },
9142
9163
  // ---- githubAuth:*(本机 GitHub 身份,机器级;spec 2026-09-15-github-auth-unify-design §4.3)----
9143
9164
  "githubAuth:get": {
9144
9165
  summary: "\u672C\u673A\u7684 GitHub \u8EAB\u4EFD\uFF1Aauthorized\uFF08@login\uFF0C\u6765\u81EA git \u51ED\u636E\u5B58\u50A8 / \u672C\u673A\u51ED\u636E\u8868\uFF09/ missing / invalid\uFF08GitHub \u4E0D\u8BA4\uFF09/ pending\uFF08\u7B49 GitHub \u8DF3\u56DE\u4E2D\u5FC3\uFF09\u3002\u8BBE\u7F6E\u9875\u300C\u8D26\u53F7\u300D\u3001\u4E0A\u4E91\u9875\u9876\u4E0A\u90A3\u5F20\u5361\u3001PR \u6536\u4EF6\u7BB1\u90FD\u770B\u5B83\uFF1Bpending \u671F\u9875\u9762\u6BCF 2 \u79D2\u8F6E\u8BE2\uFF0Cdaemon \u987A\u624B\u5411\u4E2D\u5FC3\u9886 token\u3002\u65E0\u53C2\u6570",
@@ -56076,13 +56097,13 @@ async function A2(A4 = {}) {
56076
56097
  (function() {
56077
56098
  }).bind();
56078
56099
  var E3, i2, D3, F3, o2, w3, h2, S3, y3, k3, c2, M3 = function() {
56079
- }.bind(), U2 = false, G2 = false;
56100
+ }.bind(), U3 = false, G2 = false;
56080
56101
  function J2() {
56081
56102
  var A5 = d2.buffer;
56082
56103
  h2 = new Int8Array(A5), o2 = new Int16Array(A5), g3.HEAPU8 = c2 = new Uint8Array(A5), new Uint16Array(A5), w3 = new Int32Array(A5), k3 = new Uint32Array(A5), S3 = new Float32Array(A5), y3 = new Float64Array(A5);
56083
56104
  }
56084
56105
  function Y2(A5) {
56085
- g3.onAbort?.(A5), M3(A5 = `Aborted(${A5})`), U2 = true, A5 += ". Build with -sASSERTIONS for more info.";
56106
+ g3.onAbort?.(A5), M3(A5 = `Aborted(${A5})`), U3 = true, A5 += ". Build with -sASSERTIONS for more info.";
56086
56107
  var I4 = new WebAssembly.RuntimeError(A5);
56087
56108
  throw D3?.(I4), I4;
56088
56109
  }
@@ -56240,7 +56261,7 @@ async function A2(A4 = {}) {
56240
56261
  })(0, F3, C3)).instance));
56241
56262
  })(), (function() {
56242
56263
  function A5() {
56243
- g3.calledRun = true, U2 || (G2 = true, i2?.(g3), g3.onRuntimeInitialized?.(), (function() {
56264
+ g3.calledRun = true, U3 || (G2 = true, i2?.(g3), g3.onRuntimeInitialized?.(), (function() {
56244
56265
  if (g3.postRun) for ("function" == typeof g3.postRun && (g3.postRun = [g3.postRun]); g3.postRun.length; ) H2(g3.postRun.shift());
56245
56266
  N2(K2);
56246
56267
  })());
@@ -59596,6 +59617,9 @@ function createGithubApi(args) {
59596
59617
  },
59597
59618
  async rerunJob(slug, jobId) {
59598
59619
  await must("POST", `/repos/${slug}/actions/jobs/${jobId}/rerun`, "\u91CD\u8DD1\u8FD9\u4E00\u6BB5");
59620
+ },
59621
+ async dispatchWorkflow(slug, file, ref) {
59622
+ await must("POST", `/repos/${slug}/actions/workflows/${encodeURIComponent(file)}/dispatches`, "\u89E6\u53D1\u6D41\u6C34\u7EBF", { ref });
59599
59623
  }
59600
59624
  };
59601
59625
  }
@@ -59663,7 +59687,10 @@ async function storeGithubAuth(args) {
59663
59687
  init_protocol();
59664
59688
  var CLAWOS_REPO = "ottin4ttc/clawos";
59665
59689
  var CLAWOS_REF = "release";
59666
- var WORKFLOW_PATH = ".github/workflows/clawd-deploy.yml";
59690
+ var DEPLOY_WORKFLOW_FILE = "clawd-deploy.yml";
59691
+ var WORKFLOW_PATH = `.github/workflows/${DEPLOY_WORKFLOW_FILE}`;
59692
+ var UNPUBLISH_WORKFLOW_FILE = "clawd-unpublish.yml";
59693
+ var UNPUBLISH_WORKFLOW_PATH = `.github/workflows/${UNPUBLISH_WORKFLOW_FILE}`;
59667
59694
  var DOCKERFILE_PATH = "Dockerfile";
59668
59695
  var DOCKERIGNORE_PATH = ".dockerignore";
59669
59696
  var ACR_REGISTRY = "registry.cn-hangzhou.aliyuncs.com";
@@ -59757,6 +59784,7 @@ var ACR_LOGIN_STEPS = [
59757
59784
  ];
59758
59785
  var IMAGE_LATEST = '"$REGISTRY/$ACR_NAMESPACE/${{ steps.plan.outputs.persona_id }}:latest"';
59759
59786
  var RESOLVE_LINE = ` IMAGE_REF=$(node clawos/cloud-host/pipeline/acr.mjs resolve --image ${IMAGE_LATEST} --vpc-registry "$VPC_REGISTRY")`;
59787
+ var CONCURRENCY_GROUP = " group: persona-cloud-${{ github.repository }}";
59760
59788
  var L2 = [
59761
59789
  "name: clawd deploy",
59762
59790
  "",
@@ -59777,7 +59805,7 @@ var L2 = [
59777
59805
  "",
59778
59806
  "concurrency:",
59779
59807
  " # \u540C\u4E00\u4E2A\u4ED3\u5E93\u7684\u53D1\u5E03\u6392\u961F\u8DD1\uFF0C\u4E0D\u53D6\u6D88\uFF1A\u4E0A\u4E00\u6B21\u53EF\u80FD\u5DF2\u7ECF\u5728\u767B\u8BB0\u6B63\u5F0F\u4E86",
59780
- " group: persona-cloud-${{ github.repository }}",
59808
+ CONCURRENCY_GROUP,
59781
59809
  "",
59782
59810
  "env:",
59783
59811
  ` REGISTRY: ${ACR_REGISTRY}`,
@@ -59913,6 +59941,44 @@ var L2 = [
59913
59941
  ""
59914
59942
  ];
59915
59943
  var WORKFLOW_TEMPLATE = L2.join("\n");
59944
+ var U2 = [
59945
+ "name: clawd unpublish",
59946
+ "",
59947
+ "# \u7531 clawd \u751F\u6210\u3001\u7531 clawd \u7EF4\u62A4\uFF1A\u6BCF\u6B21\u300C\u53D1\u5E03\u300D\u90FD\u4F1A\u7528\u5F53\u524D\u6A21\u677F\u91CD\u5199\u8FD9\u4EFD\u6587\u4EF6\u518D\u63D0\u4EA4\uFF0C\u624B\u6539\u4F1A\u88AB\u76D6\u6389\u3002",
59948
+ "# \u4E0B\u7EBF\uFF1A\u5728 gateway \u64A4\u6389\u8FD9\u4E2A persona \u7684\u6B63\u5F0F\u767B\u8BB0\u2014\u2014\u522B\u4EBA\u770B\u4E0D\u5230\u3001\u7528\u4E0D\u4E86\uFF1B\u4ED3\u5E93\u548C\u955C\u50CF\u90FD\u4E0D\u52A8\uFF0C\u91CD\u65B0\u53D1\u5E03\u5C31\u56DE\u6765\u3002",
59949
+ "# \u53EA\u7531 clawd \u4E0A\u4E91\u9875\u7684\u300C\u4E0B\u7EBF\u300D\u89E6\u53D1\uFF08workflow_dispatch\uFF09\uFF0C\u63A8\u4EE3\u7801\u4E0D\u8DD1\u3002\u6B63\u5728\u8DD1\u7684\u5BF9\u8BDD\u4E0D\u6253\u65AD\uFF0C\u8DD1\u5230\u7A7A\u95F2\u81EA\u5DF1\u9000\u3002",
59950
+ "#",
59951
+ "# \u8981\u7684\u7EC4\u7EC7\u7EA7 Secrets\uFF1ACLAWOS_READ_TOKEN\uFF08\u8BFB clawos \u4ED3\u5E93\uFF09\u3001CLAWD_GATEWAY_ADMIN_KEY\uFF1B\u7EC4\u7EC7\u7EA7 Variable\uFF1ACLAWD_GATEWAY_URL\u3002",
59952
+ "",
59953
+ "on:",
59954
+ " workflow_dispatch:",
59955
+ "",
59956
+ "concurrency:",
59957
+ " # \u4E0E\u53D1\u5E03\u540C\u4E00\u7EC4\uFF1A\u53D1\u5E03\u5728\u8DD1\u65F6\u70B9\u4E0B\u7EBF\uFF0C\u6392\u5728\u5B83\u540E\u9762\uFF1B\u540E\u70B9\u7684\u9876\u6389\u5148\u70B9\u7684",
59958
+ CONCURRENCY_GROUP,
59959
+ "",
59960
+ "env:",
59961
+ ` CLAWOS_REPO: ${CLAWOS_REPO}`,
59962
+ ` CLAWOS_REF: ${CLAWOS_REF}`,
59963
+ "",
59964
+ "permissions:",
59965
+ " contents: read",
59966
+ "",
59967
+ "jobs:",
59968
+ " unpublish:",
59969
+ " name: unpublish",
59970
+ " runs-on: ubuntu-latest",
59971
+ " timeout-minutes: 10",
59972
+ " steps:",
59973
+ ...CONTRACT_STEPS,
59974
+ " - name: \u64A4\u767B\u8BB0",
59975
+ " env:",
59976
+ " CLAWD_GATEWAY_URL: ${{ vars.CLAWD_GATEWAY_URL }}",
59977
+ " CLAWD_GATEWAY_ADMIN_KEY: ${{ secrets.CLAWD_GATEWAY_ADMIN_KEY }}",
59978
+ " run: node clawos/cloud-host/pipeline/unregister.mjs --persona-id '${{ steps.plan.outputs.persona_id }}'",
59979
+ ""
59980
+ ];
59981
+ var UNPUBLISH_WORKFLOW_TEMPLATE = U2.join("\n");
59916
59982
 
59917
59983
  // src/deploy/exec.ts
59918
59984
  var import_node_child_process9 = require("child_process");
@@ -60028,9 +60094,11 @@ async function writeGitignore(dir) {
60028
60094
  await import_promises6.default.writeFile(gi, mergeGitignore(existing));
60029
60095
  }
60030
60096
  async function writeWorkflow(dir) {
60031
- const wf = import_node_path36.default.join(dir, WORKFLOW_PATH);
60032
- await import_promises6.default.mkdir(import_node_path36.default.dirname(wf), { recursive: true });
60033
- await import_promises6.default.writeFile(wf, WORKFLOW_TEMPLATE);
60097
+ for (const [rel, body] of [[WORKFLOW_PATH, WORKFLOW_TEMPLATE], [UNPUBLISH_WORKFLOW_PATH, UNPUBLISH_WORKFLOW_TEMPLATE]]) {
60098
+ const wf = import_node_path36.default.join(dir, rel);
60099
+ await import_promises6.default.mkdir(import_node_path36.default.dirname(wf), { recursive: true });
60100
+ await import_promises6.default.writeFile(wf, body);
60101
+ }
60034
60102
  }
60035
60103
  async function writeDockerfile(dir, personaId2) {
60036
60104
  await import_promises6.default.writeFile(import_node_path36.default.join(dir, DOCKERFILE_PATH), renderDockerfile(personaId2));
@@ -60109,6 +60177,10 @@ async function hasCommit(exec, dir, sha) {
60109
60177
  const r2 = await git(exec, dir, ["cat-file", "-e", `${sha}^{commit}`]);
60110
60178
  return !r2.spawnError && r2.code === 0;
60111
60179
  }
60180
+ async function fileInCommit(exec, dir, sha, rel) {
60181
+ const r2 = await git(exec, dir, ["cat-file", "-e", `${sha}:${rel}`]);
60182
+ return !r2.spawnError && r2.code === 0;
60183
+ }
60112
60184
  async function commitAll(exec, args) {
60113
60185
  await mustGit(exec, args.dir, ["add", "-A"], "\u6682\u5B58\u6587\u4EF6");
60114
60186
  const staged = await git(exec, args.dir, ["diff", "--cached", "--quiet"]);
@@ -60147,6 +60219,7 @@ var SCAN_BASE_LABEL = {
60147
60219
 
60148
60220
  // src/deploy/run-status.ts
60149
60221
  init_protocol();
60222
+ var runsOf = (slug, file, query) => `/repos/${slug}/actions/workflows/${encodeURIComponent(file)}/runs?${query}`;
60150
60223
  var pipelineJobKey = (name) => DEPLOY_STAGE_KEYS.find((k3) => name === k3 || name.startsWith(`${k3} `));
60151
60224
  function stageStatusOf(status, conclusion) {
60152
60225
  if (status === "queued" || status === "requested" || status === "pending") return "pending";
@@ -60188,7 +60261,7 @@ async function runByHead(api, slug, head) {
60188
60261
  return fetchRun(api, slug, `head_sha=${encodeURIComponent(head)}&per_page=1`);
60189
60262
  }
60190
60263
  async function fetchRun(api, slug, query) {
60191
- const listed = await api.getJson(`/repos/${slug}/actions/runs?${query}`);
60264
+ const listed = await api.getJson(runsOf(slug, DEPLOY_WORKFLOW_FILE, query));
60192
60265
  const raw = listed?.workflow_runs?.[0];
60193
60266
  if (!raw || typeof raw.id !== "number" || typeof raw.head_sha !== "string" || typeof raw.html_url !== "string") {
60194
60267
  return void 0;
@@ -60210,6 +60283,21 @@ async function fetchRun(api, slug, query) {
60210
60283
  jobs: DEPLOY_STAGE_KEYS.map((k3) => byKey.get(k3)).filter((j2) => j2 !== void 0)
60211
60284
  };
60212
60285
  }
60286
+ async function latestUnpublish(api, slug) {
60287
+ const listed = await api.getJson(runsOf(slug, UNPUBLISH_WORKFLOW_FILE, "per_page=1"));
60288
+ const runs = listed?.workflow_runs;
60289
+ if (!Array.isArray(runs)) return void 0;
60290
+ const raw = runs[0];
60291
+ if (!raw) return null;
60292
+ if (typeof raw.id !== "number" || typeof raw.html_url !== "string") return void 0;
60293
+ const status = runStatusOf(String(raw.status ?? ""));
60294
+ return {
60295
+ runId: raw.id,
60296
+ status,
60297
+ htmlUrl: raw.html_url,
60298
+ ...status === "completed" ? { conclusion: raw.conclusion === "success" ? "success" : "failure" } : {}
60299
+ };
60300
+ }
60213
60301
  async function rerunJob(api, slug, jobId) {
60214
60302
  await api.rerunJob(slug, jobId);
60215
60303
  }
@@ -60241,6 +60329,8 @@ function reconcileInterruptedScans(deps) {
60241
60329
  // src/handlers/deploy.ts
60242
60330
  var SECRETS_CACHE_MS = 2e4;
60243
60331
  var RUN_CACHE_MS = 4e3;
60332
+ var ONLINE_CACHE_MS = 2e4;
60333
+ var UNPUBLISH_NEEDS_REPUBLISH = "\u8FD9\u4E2A persona \u7684\u6D41\u6C34\u7EBF\u662F\u65E7\u7248\u7684\uFF0C\u5148\u91CD\u65B0\u626B\u63CF\u3001\u53D1\u5E03\u4E00\u6B21\u518D\u4E0B\u7EBF";
60244
60334
  function ensureOwner(ctx) {
60245
60335
  if (!ctx || ctx.principal.kind !== "owner") {
60246
60336
  throw new ClawdError(ERROR_CODES.UNAUTHORIZED, "UNAUTHORIZED: deploy:* requires owner ctx");
@@ -60301,6 +60391,28 @@ function buildDeployHandlers(deps) {
60301
60391
  runCache.set(key, { at: deps.now(), run });
60302
60392
  return run;
60303
60393
  };
60394
+ const unpublishCache = /* @__PURE__ */ new Map();
60395
+ const cachedUnpublish = async (personaId2, api, slug) => {
60396
+ const hit = unpublishCache.get(personaId2);
60397
+ if (hit && deps.now() - hit.at < RUN_CACHE_MS) return hit.run;
60398
+ const run = await latestUnpublish(api, slug) ?? void 0;
60399
+ unpublishCache.set(personaId2, { at: deps.now(), run });
60400
+ return run;
60401
+ };
60402
+ const onlineCache = /* @__PURE__ */ new Map();
60403
+ const cachedOnline = async (personaId2, run, unpublish) => {
60404
+ const stamp = `${run?.runId ?? "-"}:${run?.status ?? "-"}|${unpublish?.runId ?? "-"}:${unpublish?.status ?? "-"}`;
60405
+ const hit = onlineCache.get(personaId2);
60406
+ if (hit && hit.stamp === stamp && deps.now() - hit.at < ONLINE_CACHE_MS) return hit.online;
60407
+ let online;
60408
+ try {
60409
+ online = await deps.cloudOnline(personaId2);
60410
+ } catch {
60411
+ online = void 0;
60412
+ }
60413
+ onlineCache.set(personaId2, { stamp, at: deps.now(), online });
60414
+ return online;
60415
+ };
60304
60416
  deps.githubIdentity.onChange(() => secretsCache.clear());
60305
60417
  async function requireGithub() {
60306
60418
  const auth = await deps.githubIdentity.refresh();
@@ -60337,6 +60449,7 @@ function buildDeployHandlers(deps) {
60337
60449
  const { scans } = file;
60338
60450
  let secrets;
60339
60451
  let run;
60452
+ let unpublish;
60340
60453
  if (repo2 && api && github.status === "authorized") {
60341
60454
  try {
60342
60455
  secrets = await cachedSecrets(personaId2, dir, api);
@@ -60344,7 +60457,9 @@ function buildDeployHandlers(deps) {
60344
60457
  secrets = void 0;
60345
60458
  }
60346
60459
  run = await cachedRun(personaId2, api, repoSlug(repo2.url), releaseHead);
60460
+ if (releaseHead === void 0) unpublish = await cachedUnpublish(personaId2, api, repoSlug(repo2.url));
60347
60461
  }
60462
+ const online = releaseHead === void 0 && file.releases.length > 0 ? await cachedOnline(personaId2, run, unpublish) : void 0;
60348
60463
  return {
60349
60464
  personaId: personaId2,
60350
60465
  github,
@@ -60353,6 +60468,8 @@ function buildDeployHandlers(deps) {
60353
60468
  releases: file.releases,
60354
60469
  ...secrets ? { secrets } : {},
60355
60470
  ...run ? { run } : {},
60471
+ ...online !== void 0 ? { online } : {},
60472
+ ...unpublish ? { unpublish } : {},
60356
60473
  ...historyError ? { historyError } : {}
60357
60474
  };
60358
60475
  }
@@ -60657,6 +60774,27 @@ function buildDeployHandlers(deps) {
60657
60774
  }
60658
60775
  return view(personaId2);
60659
60776
  };
60777
+ const unpublishHandler = async (frame, _client, ctx) => {
60778
+ ensureOwner(ctx);
60779
+ const { personaId: personaId2 } = parseArgs2(DeployPersonaArgsSchema, frame, "deploy:unpublish");
60780
+ const dir = mustPersona(personaId2);
60781
+ const repo2 = await repoInfo(deps.exec, dir);
60782
+ if (!repo2) throw invalid("\u8FD9\u4E2A persona \u8FD8\u6CA1\u4E0A\u8FC7\u4E91");
60783
+ const { api } = await requireGithub();
60784
+ const last = (await readHistory(dir)).releases.at(-1);
60785
+ if (!last || !await fileInCommit(deps.exec, dir, last.head, UNPUBLISH_WORKFLOW_PATH)) throw invalid(UNPUBLISH_NEEDS_REPUBLISH);
60786
+ const slug = repoSlug(repo2.url);
60787
+ const before = await latestUnpublish(api, slug);
60788
+ if (before === void 0) throw invalid("\u67E5\u4E0D\u5230\u8FD9\u4E2A\u4ED3\u5E93\u4E0A\u7684\u4E0B\u7EBF\u6D41\u6C34\u7EBF\uFF08GitHub \u6CA1\u54CD\u5E94\uFF09\uFF0C\u7A0D\u540E\u518D\u70B9\u4E00\u6B21");
60789
+ try {
60790
+ await api.dispatchWorkflow(slug, UNPUBLISH_WORKFLOW_FILE, "main");
60791
+ } catch (err) {
60792
+ throw new ClawdError(ERROR_CODES.INTERNAL, err instanceof Error ? err.message : String(err));
60793
+ }
60794
+ unpublishCache.delete(personaId2);
60795
+ onlineCache.delete(personaId2);
60796
+ return { response: { type: "deploy:unpublish", before: before?.runId ?? null } };
60797
+ };
60660
60798
  return {
60661
60799
  "deploy:get": get,
60662
60800
  "deploy:scan": scanHandler,
@@ -60665,7 +60803,8 @@ function buildDeployHandlers(deps) {
60665
60803
  "deploy:reportFinding": reportFinding,
60666
60804
  "deploy:finishScan": finishScan,
60667
60805
  "deploy:release": releaseHandler,
60668
- "deploy:rerunJob": rerunJobHandler
60806
+ "deploy:rerunJob": rerunJobHandler,
60807
+ "deploy:unpublish": unpublishHandler
60669
60808
  };
60670
60809
  }
60671
60810
 
@@ -63205,13 +63344,17 @@ var CloudGatewayClient = class {
63205
63344
  if (!apiKey) throw new CloudApiKeyRequiredError();
63206
63345
  return { apiKey, gatewayUrl };
63207
63346
  }
63208
- async listPersonas() {
63347
+ /**
63348
+ * `healthzTimeoutMs`:只给 /healthz 那一趟定上限,**问到地址之后才开始计**(上云页的线上状态用;design 2026-09-24-deploy-unpublish)。
63349
+ * 中心那一跳(locate)有它自己的上限,不归这里管。不给 = 同以前,不设
63350
+ */
63351
+ async listPersonas(opts = {}) {
63209
63352
  const { gatewayUrl } = await this.locate();
63210
- return this.fetchDeployments(gatewayUrl);
63353
+ return this.fetchDeployments(gatewayUrl, opts.healthzTimeoutMs ? AbortSignal.timeout(opts.healthzTimeoutMs) : void 0);
63211
63354
  }
63212
63355
  /** /healthz 的登记表 → official 行。gatewayUrl 由调用方定:listPersonas 从中心拿,personaConnectors 用 persona 文件里的 */
63213
- async fetchDeployments(gatewayUrl) {
63214
- const res = await this.fetchImpl(gatewayUrl + GATEWAY_ROUTES.healthz());
63356
+ async fetchDeployments(gatewayUrl, signal) {
63357
+ const res = await this.fetchImpl(gatewayUrl + GATEWAY_ROUTES.healthz(), ...signal ? [{ signal }] : []);
63215
63358
  if (!res.ok) throw new Error(`gateway /healthz \u56DE ${res.status}`);
63216
63359
  const body = await res.json();
63217
63360
  const rows = Array.isArray(body.deployments) ? body.deployments : [];
@@ -71686,6 +71829,7 @@ var METHOD_GRANT_MAP = {
71686
71829
  "deploy:finishScan": ADMIN_ANY,
71687
71830
  "deploy:release": ADMIN_ANY,
71688
71831
  "deploy:rerunJob": ADMIN_ANY,
71832
+ "deploy:unpublish": ADMIN_ANY,
71689
71833
  // cloud:connectors:*(云 persona 卡片上的连接器;design 2026-09-11-mcp-connector):
71690
71834
  // 拿本机登录凭证向中心代查 / 代填使用者本人的凭据,guest 一律够不着
71691
71835
  "cloud:connectors:list": ADMIN_ANY,
@@ -71899,6 +72043,7 @@ var HUMAN_ONLY_METHODS = [
71899
72043
  "deploy:release",
71900
72044
  "deploy:putSecret",
71901
72045
  "deploy:rerunJob",
72046
+ "deploy:unpublish",
71902
72047
  "githubAuth:oauthStart",
71903
72048
  "githubAuth:authorize"
71904
72049
  ];
@@ -71943,7 +72088,7 @@ function computeMethodAccess(args) {
71943
72088
  }
71944
72089
 
71945
72090
  // src/version.ts
71946
- var version = "0.2.581".length > 0 ? "0.2.581" : "dev";
72091
+ var version = "0.2.582".length > 0 ? "0.2.582" : "dev";
71947
72092
 
71948
72093
  // src/cli-probe/probe.ts
71949
72094
  var fs80 = __toESM(require("fs"), 1);
@@ -75626,6 +75771,13 @@ async function startDaemon(config) {
75626
75771
  org: PERSONA_REPO_ORG,
75627
75772
  now: () => Date.now(),
75628
75773
  warn: (msg, meta) => logger.warn(msg, meta),
75774
+ // 在不在线上 = gateway 正式登记表里有没有它(design 2026-09-24-deploy-unpublish §3.3):跟 persona 列表的云卡片同一个来源。
75775
+ // 拉不到(没登录飞书 / gateway 不通 / healthz 5s 没回)→ undefined,页面不给下线按钮也不撤「线上」标。
75776
+ // 失败上界:中心那一跳自带 15s,healthz 这一趟 5s——deploy:get 要保证页面打得开,gateway 接了连接不回时不能挂到 undici 默认的 300s
75777
+ cloudOnline: (personaId2) => cloudGateway.listPersonas({ healthzTimeoutMs: 5e3 }).then(
75778
+ (list) => list.some((p3) => p3.personaId === personaId2),
75779
+ () => void 0
75780
+ ),
75629
75781
  startScan: (personaId2, scanBase, scanId) => {
75630
75782
  const baseLines = scanBase.base ? [
75631
75783
  `\u672C\u6B21\u662F**${SCAN_BASE_LABEL[scanBase.kind]}**\uFF1A\u53EA\u770B\u8DDF\u7EBF\u4E0A\u6B63\u8DD1\u90A3\u7248\u7684\u5DEE\u522B\uFF0C`,