@clawos-dev/clawd 0.2.459 → 0.2.461

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.
@@ -6893,6 +6893,7 @@ __export(mcp_server_exports, {
6893
6893
  handleCreateProject: () => handleCreateProject,
6894
6894
  handleListProjects: () => handleListProjects,
6895
6895
  handlePublish: () => handlePublish,
6896
+ handleRemoveProject: () => handleRemoveProject,
6896
6897
  handleReportStage: () => handleReportStage,
6897
6898
  handleSetProdUrl: () => handleSetProdUrl,
6898
6899
  handleStartDevServer: () => handleStartDevServer,
@@ -21361,6 +21362,19 @@ var DeployGapSchema = external_exports.object({
21361
21362
  // 建议补的依赖:`cli:python3` 这种。拿不准就不给,让人自己决定。
21362
21363
  suggest: external_exports.string().optional()
21363
21364
  }).strict();
21365
+ var DeployPlacementSchema = external_exports.object({
21366
+ /** gateway 的公网地址(入口 A 直接 POST 它;入口 B 由设备目录下发 ws 地址) */
21367
+ gatewayUrl: external_exports.string().min(1),
21368
+ /** 服务设备 id:别人的 clawd 用 `device:connect { deviceId }` 就能连上,本机侧零改动 */
21369
+ serviceDeviceId: external_exports.string().min(1),
21370
+ /** 这次登记为正式的镜像 tag(= 试装通过的那个) */
21371
+ image: external_exports.string().min(1),
21372
+ /** 使用者自带的凭据名字(= 单据里 fulfillment 选了 'user' 的那些)。**只有名字** */
21373
+ userSecrets: external_exports.array(external_exports.string()).default([]),
21374
+ /** 是否进公共设备目录。false = 只有拿到 deviceId 的人连得上 */
21375
+ publicDirectory: external_exports.boolean(),
21376
+ deployedAt: external_exports.number()
21377
+ }).strict();
21364
21378
  var DeployReceiptSchema = external_exports.object({
21365
21379
  deployId: external_exports.string().min(1),
21366
21380
  personaId: external_exports.string().min(1),
@@ -21386,6 +21400,23 @@ var DeployReceiptSchema = external_exports.object({
21386
21400
  fulfillment: external_exports.record(external_exports.string(), FulfillmentSchema).default({}),
21387
21401
  /** 试装失败时的缺口清单;成功或没试装过是空数组 */
21388
21402
  gaps: external_exports.array(DeployGapSchema).default([]),
21403
+ /** 试装跑到第几轮了(每次 deploy:trial +1,上限 TRIAL_ROUND_MAX)。重扫清零 */
21404
+ trialRound: external_exports.number().default(0),
21405
+ /**
21406
+ * 试装当前在哪一步的一句人话(「在构镜像」「冒烟任务跑起来了」)。
21407
+ * 单据页要的是「到哪一步了」,不是逐 token 的过程——过程在 gateway 的任务台账里。
21408
+ */
21409
+ trialStage: external_exports.string().optional(),
21410
+ /**
21411
+ * 上一次试装通过的时刻。**deploy:deploy 的前置**——没试装过不让部署,那正是试装存在的理由。
21412
+ * 试装通过后单据回 `ready` 而不是直接 `deployed`:试装的产物是一个用完即弃的镜像,
21413
+ * 部署的产物是一个别人能派活的服务,中间那次决定得留给人。
21414
+ */
21415
+ trialPassedAt: external_exports.number().optional(),
21416
+ /** 试装通过的那个镜像 tag;deploy:deploy 登记的就是它,不重新构一遍 */
21417
+ trialImage: external_exports.string().optional(),
21418
+ /** 部署落点(status 是 deployed 才有) */
21419
+ placement: DeployPlacementSchema.optional(),
21389
21420
  // 扫描 / 试装失败的原因(status 是 scanFailed / failed 时才有意义)
21390
21421
  error: external_exports.string().optional(),
21391
21422
  createdAt: external_exports.number(),
@@ -22071,6 +22102,21 @@ var TOPIC_DECISION_STATUS_RESOLVED = TOPIC_DECISION_STATUSES[1];
22071
22102
  var TopicDecisionTypeSchema = external_exports.enum(TOPIC_DECISION_TYPES);
22072
22103
  var TopicDecisionStatusSchema = external_exports.enum(TOPIC_DECISION_STATUSES);
22073
22104
  var TopicDecisionSchema = external_exports.object({
22105
+ /**
22106
+ * 这条决策的标识,供后来的答复回指。可选:存量记录一条都没有,**不迁移**。
22107
+ *
22108
+ * 之所以要它——提问和答复是**两条**流水(`open` 一条、`resolved` 一条,见 `resolves`),
22109
+ * 两条之间没有任何关联时,展示层只能逐条渲染,于是**已经拍板完的待办永远挂在那儿**。
22110
+ */
22111
+ id: nonBlank2.optional(),
22112
+ /**
22113
+ * 本条解决的是哪条决策(填对方的 `id`)。展示层据此把提问与答复合并成一行。
22114
+ *
22115
+ * **只关联、不改写**:不去改动已落盘那条 `open` 记录的 status。原地改写会丢掉
22116
+ * 「什么时候提出 / 什么时候解决」的双时间戳,也丢掉「Master 建议了什么 vs
22117
+ * 老板最终定了什么」的差异——而老板事后要看的恰恰是这个差异。合并只发生在展示层。
22118
+ */
22119
+ resolves: nonBlank2.optional(),
22074
22120
  type: TopicDecisionTypeSchema,
22075
22121
  status: TopicDecisionStatusSchema,
22076
22122
  question: nonBlank2,
@@ -23914,6 +23960,14 @@ var METHOD_DOCS = {
23914
23960
  summary: "butler \u6536\u5C3E\uFF1A\u7ED9\u51FA\u5192\u70DF\u4EFB\u52A1\u4E0E\u5B58\u7591\u9879\uFF0C\u5355\u636E\u4ECE scanning \u8F6C draft\u3002\u6CA1\u8C03\u5230\u5B83\u5C31\u7B49\u4E8E\u6CA1\u626B\u5B8C\u2014\u2014butler \u4F1A\u8BDD\u7ED3\u675F\u65F6\u5355\u636E\u8FD8\u505C\u5728 scanning\uFF0Cdaemon \u636E\u6B64\u5224 scanFailed\uFF1Bowner-only",
23915
23961
  args: DeployFinishScanArgsSchema
23916
23962
  },
23963
+ "deploy:trial": {
23964
+ summary: "\u8D77\u4E00\u6B21\u8BD5\u88C5\uFF1A**\u4E00\u6B21\u5E26\u81EA\u6BC1\u7684\u771F\u90E8\u7F72**\u2014\u2014\u6309\u5355\u636E\u7684\u6E05\u5355\u6784\u955C\u50CF \u2192 \u63A8\u8FDB k3s \u2192 \u7ECF gateway \u6295\u5582\u5355\u636E\u4E0A\u90A3\u6761\u5192\u70DF\u4EFB\u52A1 \u2192 \u8BA9 persona \u7528\u81EA\u5DF1\u7684\u65B9\u5F0F\u5E72 \u2192 \u8DD1\u5B8C\u8BA9\u5B83\u628A\u5EFA\u51FA\u6765\u7684\u4E1C\u897F\u62C6\u6389\u3002\u524D\u7F6E\u662F `ready`\uFF08\u5C65\u7EA6\u90FD\u9009\u4E86\u3001\u9009\u4E86\u300C\u6211\u6765\u63D0\u4F9B\u300D\u7684\u51ED\u636E\u90FD\u586B\u9F50\u4E86\uFF09\uFF0C\u7F3A\u54EA\u6761\u4F1A\u5728\u9519\u8BEF\u91CC\u70B9\u540D\uFF1B\u8DD1\u51E0\u5341\u5206\u949F\uFF0C\u6240\u4EE5 RPC \u79D2\u56DE `trialing` \u6001\u5355\u636E\uFF0C\u7ED3\u679C\u7531\u540E\u53F0\u56DE\u5199\uFF08UI \u8F6E\u8BE2\uFF09\u3002\u901A\u8FC7 \u2192 \u56DE `ready` \u5E76\u8BB0\u4E0B trialPassedAt\uFF08\u90E8\u7F72\u662F\u4EBA\u7684\u7B2C\u4E8C\u6B21\u51B3\u5B9A\uFF09\uFF1B\u6CA1\u8FC7 \u2192 `failed` + gaps\uFF08agent \u7684\u4EBA\u8BDD\u9519\u56E0\uFF09\uFF0C\u6539\u5B8C\u6E05\u5355\u518D\u8C03\u4E00\u6B21\u5373\u53EF\u3002\u8F6E\u6B21\u6709\u4E0A\u9650\uFF1Bowner-only",
23965
+ args: DeployIdArgsSchema
23966
+ },
23967
+ "deploy:deploy": {
23968
+ summary: "\u628A\u8BD5\u88C5\u901A\u8FC7\u7684\u90A3\u4E2A\u955C\u50CF\u5728 gateway \u767B\u8BB0\u4E3A**\u6B63\u5F0F**\u90E8\u7F72\uFF1A\u4E4B\u540E\u522B\u4EBA\u7684 clawd \u6D3E\u6D3B\u7ED9\u8FD9\u4E2A\u670D\u52A1\u8BBE\u5907\uFF0C\u8D77\u7684\u5C31\u662F\u5B83\u3002\u524D\u7F6E\u662F\u8BD5\u88C5\u901A\u8FC7\u8FC7\uFF08\u6CA1\u9A8C\u8FC7\u4E0D\u8BA9\u53D1\uFF0C\u90A3\u6B63\u662F\u8BD5\u88C5\u5B58\u5728\u7684\u7406\u7531\uFF09\u3002\u56DE\u6765\u7684\u5355\u636E\u5E26 placement\u2014\u2014\u4E91\u4E0A\u5730\u5740 / \u670D\u52A1\u8BBE\u5907 id / \u8C01\u53EF\u8C03\u7528 / \u4F7F\u7528\u8005\u9700\u81EA\u5E26\u54EA\u4E9B\u51ED\u636E\uFF1Bowner-only",
23969
+ args: DeployIdArgsSchema
23970
+ },
23917
23971
  "deploy:remove": {
23918
23972
  summary: "\u5220\u6389\u4E00\u5F20\u90E8\u7F72\u5355\uFF0C\u8FDE\u5E26\u6E05\u6389\u5B83\u7684\u5168\u90E8\u51ED\u636E\u503C\uFF08\u503C\u4E0D\u8BE5\u6BD4\u5355\u636E\u6D3B\u5F97\u4E45\uFF09\uFF1Bowner-only",
23919
23973
  args: DeployIdArgsSchema
@@ -24658,6 +24712,9 @@ var ProjectStore = class {
24658
24712
  }
24659
24713
  };
24660
24714
 
24715
+ // src/app-builder-plugin/core.ts
24716
+ var import_node_child_process3 = require("child_process");
24717
+
24661
24718
  // src/app-builder-plugin/publish-registry.ts
24662
24719
  var import_node_crypto = require("crypto");
24663
24720
  var PublishJobRegistry = class {
@@ -25070,6 +25127,38 @@ async function createProjectOnDisk(args) {
25070
25127
  }
25071
25128
  return { project, projectDir: store.projectDir(name) };
25072
25129
  }
25130
+ var REMOVE_TIMEOUT_MS = 10 * 6e4;
25131
+ async function runRemoveExtension(args) {
25132
+ const spawnFn = args.spawnImpl ?? import_node_child_process3.spawn;
25133
+ return await new Promise((resolve, reject) => {
25134
+ const child = spawnFn("bash", [args.script, args.projectDir, args.personaRoot], {
25135
+ // 凭据在 process.env 里(gateway → MCP server 的 per-server env → 本进程)
25136
+ env: process.env,
25137
+ stdio: ["ignore", "pipe", "pipe"]
25138
+ });
25139
+ let stdout = "";
25140
+ let stderr = "";
25141
+ child.stdout?.on("data", (d) => {
25142
+ stdout += d.toString("utf8");
25143
+ });
25144
+ child.stderr?.on("data", (d) => {
25145
+ stderr += d.toString("utf8");
25146
+ });
25147
+ const timer = setTimeout(() => {
25148
+ child.kill("SIGKILL");
25149
+ reject(new Error(`remove-extension timeout (>${REMOVE_TIMEOUT_MS}ms): ${args.projectDir}`));
25150
+ }, REMOVE_TIMEOUT_MS);
25151
+ child.on("error", (err) => {
25152
+ clearTimeout(timer);
25153
+ reject(new Error(`remove-extension spawn failed: ${err.message}`));
25154
+ });
25155
+ child.on("exit", (code) => {
25156
+ clearTimeout(timer);
25157
+ args.logger?.info("app-builder.remove.exit", { projectDir: args.projectDir, code });
25158
+ resolve({ code, stdout, stderr });
25159
+ });
25160
+ });
25161
+ }
25073
25162
 
25074
25163
  // src/app-builder-plugin/mcp-server.ts
25075
25164
  var LOG_PATH = process.env.CLAWD_APP_BUILDER_LOG;
@@ -25102,7 +25191,8 @@ function readEnv(env = process.env) {
25102
25191
  ...env.CLAWD_APP_BUILDER_TEMPLATE_DIR ? { templateDir: env.CLAWD_APP_BUILDER_TEMPLATE_DIR } : {},
25103
25192
  ...env.CLAWD_APP_BUILDER_SCAFFOLD_SCRIPT ? { scaffoldScript: env.CLAWD_APP_BUILDER_SCAFFOLD_SCRIPT } : {},
25104
25193
  ...env.CLAWD_APP_BUILDER_PUBLISH_SCRIPT ? { publishScript: env.CLAWD_APP_BUILDER_PUBLISH_SCRIPT } : {},
25105
- ...env.CLAWD_APP_BUILDER_PERSONA_ROOT ? { personaRoot: env.CLAWD_APP_BUILDER_PERSONA_ROOT } : {}
25194
+ ...env.CLAWD_APP_BUILDER_PERSONA_ROOT ? { personaRoot: env.CLAWD_APP_BUILDER_PERSONA_ROOT } : {},
25195
+ ...env.CLAWD_APP_BUILDER_REMOVE_SCRIPT ? { removeScript: env.CLAWD_APP_BUILDER_REMOVE_SCRIPT } : {}
25106
25196
  };
25107
25197
  }
25108
25198
  function hasDaemon(env) {
@@ -25263,6 +25353,35 @@ ${outcome.errorSummary}`));
25263
25353
  }
25264
25354
  return await done;
25265
25355
  }
25356
+ async function handleRemoveProject(input, ctx) {
25357
+ if (hasDaemon(ctx.env)) {
25358
+ return fail(
25359
+ 'removeProject is cloud-only. On this host run the script yourself:\n bash "$HOME/.clawd/deploy-kit/scripts/remove-extension.sh" <projectDir> <personaRoot>'
25360
+ );
25361
+ }
25362
+ const { removeScript, personaRoot } = ctx.env;
25363
+ if (!removeScript) {
25364
+ return fail("removeProject: no daemon, and CLAWD_APP_BUILDER_REMOVE_SCRIPT is not set.");
25365
+ }
25366
+ const store = storeOf(ctx.env);
25367
+ try {
25368
+ const projectDir = store.projectDir(input.name);
25369
+ const r = await runRemoveExtension({
25370
+ script: removeScript,
25371
+ projectDir,
25372
+ personaRoot: personaRoot ?? "",
25373
+ logger: { info: (m, i) => log(m, i), warn: (m, i) => log(m, i) }
25374
+ });
25375
+ const text = `exit=${r.code}
25376
+ --- stdout ---
25377
+ ${r.stdout}
25378
+ --- stderr ---
25379
+ ${r.stderr}`;
25380
+ return r.code === 0 ? ok(text) : fail(text);
25381
+ } catch (err) {
25382
+ return fail(`removeProject failed: ${err instanceof Error ? err.message : String(err)}`);
25383
+ }
25384
+ }
25266
25385
  function registerTools(server, ctx) {
25267
25386
  server.registerTool(
25268
25387
  "createProject",
@@ -25338,6 +25457,15 @@ function registerTools(server, ctx) {
25338
25457
  },
25339
25458
  async (input) => handlePublish(input, ctx)
25340
25459
  );
25460
+ server.registerTool(
25461
+ "removeProject",
25462
+ {
25463
+ title: "Tear down a published project",
25464
+ description: "Tear down everything a published project created (FC function / trigger / custom domain, plus the DROP statements for its Supabase tables). Cloud-only: on a host with a local daemon this returns the shell command to run yourself instead. Returns the script output verbatim \u2014 read it, it may ask you to run a few DROPs through the supabase MCP to finish the job.",
25465
+ inputSchema: { name: external_exports.string().min(1).describe("project name") }
25466
+ },
25467
+ async (input) => handleRemoveProject(input, ctx)
25468
+ );
25341
25469
  }
25342
25470
  async function main() {
25343
25471
  const env = readEnv();
@@ -25364,6 +25492,7 @@ if (isMainModule) {
25364
25492
  handleCreateProject,
25365
25493
  handleListProjects,
25366
25494
  handlePublish,
25495
+ handleRemoveProject,
25367
25496
  handleReportStage,
25368
25497
  handleSetProdUrl,
25369
25498
  handleStartDevServer,