@clawos-dev/clawd 0.2.512 → 0.2.514

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.
package/dist/cli.cjs CHANGED
@@ -7710,7 +7710,7 @@ var init_principal = __esm({
7710
7710
  });
7711
7711
 
7712
7712
  // ../protocol/src/schemas.ts
7713
- var SessionStatusSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, SelectOptionSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, ToolFeaturesSchema, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, TURN_END_REASONS, ParsedEventSchema, SessionCreateArgs, ProjectEntry, ProjectPatchArgs, ProjectRevealArgs, ProjectCreateArgs, SessionImportExternalArgs, SessionListArgs, SessionIdArgs, SessionUpdateArgs, QueuedMessage, SessionSendArgs, SessionDequeueArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionCurrentTurnSenderArgs, SessionSubscribeArgs, SessionPinArgs, SessionArchiveArgs, SessionSearchContentArgs, SessionSearchMatchSchema, SessionSearchContentResponseSchema, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, ClientInfoSchema, AuthRequestFrameSchema, AuthOkFrameSchema, DeviceConnectionEntrySchema, DeviceConnectionsResponseSchema, TunnelReadyEventSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema, PROJECT_PORT_MIN, PROJECT_PORT_MAX, projectNameRegex, DEFAULT_DEV_COMMAND, ProjectMetadataSchema, PROJECT_STAGE_VALUES, ProjectStageSchema, ProjectWithStatusSchema, ListProjectsArgsSchema, ListProjectsResultSchema, GetProjectArgsSchema, GetProjectResultSchema, CreateProjectArgsSchema, CreateProjectResultSchema, DeleteProjectArgsSchema, DeleteProjectResultSchema, UpdateProjectPortArgsSchema, UpdateProjectPortResultSchema, SetProdUrlArgsSchema, SetProdUrlResultSchema, StartDevServerArgsSchema, StartDevServerResultSchema, ASSISTANT_REPORTABLE_STAGES, ReportStageArgsSchema, ReportStageResultSchema, StopDevServerArgsSchema, StopDevServerResultSchema, AppBuilderProjectUpdatedEventSchema, PublishArgsSchema, PublishResultSchema, DismissPublishJobArgsSchema, DismissPublishJobResultSchema, AppBuilderPublishProgressEventSchema, AppBuilderPublishFailedEventSchema;
7713
+ var SessionStatusSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, SelectOptionSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, ToolFeaturesSchema, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, TURN_END_REASONS, ParsedEventSchema, SessionCreateArgs, ProjectEntry, ProjectPatchArgs, ProjectRevealArgs, ProjectCreateArgs, SessionImportExternalArgs, SessionListArgs, SessionIdArgs, SessionUpdateArgs, QueuedMessage, SessionSendArgs, SessionDequeueArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionCurrentTurnSenderArgs, SessionSubscribeArgs, SessionPinArgs, SessionArchiveArgs, SessionSearchContentArgs, SessionSearchMatchSchema, SessionSearchContentResponseSchema, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, ClientInfoSchema, AuthRequestFrameSchema, AuthOkFrameSchema, DeviceConnectionEntrySchema, DeviceConnectionsResponseSchema, TunnelReadyEventSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema;
7714
7714
  var init_schemas = __esm({
7715
7715
  "../protocol/src/schemas.ts"() {
7716
7716
  "use strict";
@@ -7861,10 +7861,6 @@ var init_schemas = __esm({
7861
7861
  * 本机资源指针——mirror 上与 ownerPersonaId 同地点 strip(#750 防线)。
7862
7862
  */
7863
7863
  projectPath: external_exports.string().min(1).optional(),
7864
- // app-builder Project 反向索引(spec: superpowers/specs/2026-06-01-app-builder-project-picker-design.md)。
7865
- // 当此 session 绑定到某个 build 中的 project 时填写;name 与 projects/<name>/ 子目录同名,
7866
- // 受 projectNameSchema regex 约束。daemon session:resume 时校验该目录存在,不存在则拒。
7867
- appBuilderProject: external_exports.string().regex(/^[a-z][a-z0-9-]{0,39}$/).optional(),
7868
7864
  // listener-mode sub-session 的原始 chatId(owner-mode 不写;listener-scope 强制写入)。
7869
7865
  // 派生 sessionId 是单向 hash `${personaId}-${tokenHash12}-${chatHash8}`,必须保留原始 chatId
7870
7866
  // 才能让 alice 重连同一 sub-session(持久化在 alice localStorage 之外,由 daemon push 同步)。
@@ -8199,10 +8195,6 @@ var init_schemas = __esm({
8199
8195
  // owner-mode persona session:传此字段时 daemon 自行派生 cwd 和启动参数,
8200
8196
  // 且按 ownerPersonaId 做 idempotent dedupe(每 persona 永远只有一个 owner session)
8201
8197
  ownerPersonaId: external_exports.string().min(1).optional(),
8202
- // 单栏默认 refactor (spec 2026-06-02):appBuilderProject 字段已从 SessionCreateArgs 砍掉。
8203
- // 绑定路径收敛到 appBuilder:createProject —— 先创建空 session(仅传 ownerPersonaId=persona-app-builder),
8204
- // 后通过 createProject RPC 创建 project 并 daemon 内部 mutate 写入 appBuilderProject。
8205
- // SessionFile schema 仍保留该字段供 daemon 内部 mutate(manager.update 内部调用通道)。
8206
8198
  /**
8207
8199
  * 新建会话选择的 project(spec 2026-08-17-projects-sidebar §4.2):
8208
8200
  * - 单独出现:daemon 以它为 cwd(等价传 cwd)
@@ -8289,9 +8281,6 @@ var init_schemas = __esm({
8289
8281
  // 从所有 sidebar 消费者视野消失(App.visibleSessions / buildSessionTree),用户看不见
8290
8282
  // 也删不掉,再被 sweepEphemeralOnStartup 静默删除——数据丢失级别的口子。
8291
8283
  ephemeral: external_exports.literal(false).optional()
8292
- // 注意:appBuilderProject 字段已从 patch 中砍掉(spec 2026-06-02-app-builder-single-pane-default-design)。
8293
- // 绑定单向不可变 —— 由 appBuilder:createProject 在 daemon 内部一次写入;UI / assistant 不再
8294
- // 通过 sessionUpdate 修改这个字段。SessionFile 仍保留 appBuilderProject 字段供 daemon 内部 mutate。
8295
8284
  })
8296
8285
  });
8297
8286
  QueuedMessage = external_exports.object({
@@ -8638,189 +8627,6 @@ var init_schemas = __esm({
8638
8627
  * 探测见 daemon/src/cli-probe/probe.ts。旧 daemon 不发 → undefined。 */
8639
8628
  globalCliVersion: external_exports.string().nullable().optional()
8640
8629
  });
8641
- PROJECT_PORT_MIN = 6173;
8642
- PROJECT_PORT_MAX = 6272;
8643
- projectNameRegex = /^[a-z][a-z0-9-]{0,39}$/;
8644
- DEFAULT_DEV_COMMAND = "cd server && pnpm dev";
8645
- ProjectMetadataSchema = external_exports.object({
8646
- name: external_exports.string().regex(projectNameRegex, {
8647
- message: "kebab-case, \u5C0F\u5199\u5B57\u6BCD\u5F00\u5934\uFF0C\u53EA\u5141\u8BB8\u5C0F\u5199\u5B57\u6BCD / \u6570\u5B57 / -\uFF0C\u6700\u957F 40 \u5B57\u7B26"
8648
- }),
8649
- port: external_exports.number().int().min(PROJECT_PORT_MIN).max(PROJECT_PORT_MAX),
8650
- createdAt: external_exports.string().datetime(),
8651
- /** dev server 启动命令(shell 字符串);占位符 $CLAWD_PREVIEW_PORT 替换 + env 注入 */
8652
- devCommand: external_exports.string().min(1),
8653
- /**
8654
- * 发布上线后的公网 URL(spec 2026-06-02-app-builder-single-pane-default-design §5.6.1)。
8655
- * assistant 跑完 publish.sh 后调 appBuilder:setProdUrl 写入;UI 读这个字段切预览栏到线上 URL,
8656
- * 取代旧 `<builder-preview url=... label="prod" />` marker 机制。
8657
- * 老 project 元数据无此字段(optional 兼容)。
8658
- */
8659
- prodUrl: external_exports.string().url().optional(),
8660
- /**
8661
- * dev server 就绪 stdout regex(spec 2026-06-02-app-builder-explicit-dev-server-control §2)。
8662
- * supervisor spawn 后会监听子进程 stdout,命中此 regex 视为 dev server 真 ready;
8663
- * 仅当 await ready 后才把 stage 切到 'running',避免 cold start race 导致 iframe 502。
8664
- * optional:缺省时 supervisor 用默认 /Local:\s+https?:\/\//(vite "Local: http://..." 输出)。
8665
- * 老板换非 vite 技术栈(Next.js "Ready in"、webpack "Compiled" 等)时自定义这个字段。
8666
- */
8667
- readyPattern: external_exports.string().optional(),
8668
- /**
8669
- * 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.1.4):
8670
- * in-flight 发布 job 的元数据。仅承载"in-flight"或"中断残骸"两种状态:
8671
- * - daemon spawn publish.sh + 收到第一个 ::stage::build marker 时写入
8672
- * - 每次 stage 切换更新 stage 字段
8673
- * - 成功 / 正常失败 / 用户 dismiss 时立即清掉
8674
- * - 只有 daemon 崩才会留残骸(子进程随 daemon 一起死)
8675
- * status 字段在 ProjectWithStatus 派生而不在这里 —— 本 schema 是落盘形态,
8676
- * "in-flight vs interrupted" 区分由 daemon 响应时实时拼 registry 状态得出。
8677
- */
8678
- publishJob: external_exports.object({
8679
- jobId: external_exports.string().min(1),
8680
- stage: external_exports.enum(["build", "deploy", "verify"]),
8681
- startedAt: external_exports.number().int().nonnegative()
8682
- }).optional()
8683
- });
8684
- PROJECT_STAGE_VALUES = [
8685
- "install-pending",
8686
- // createProject 刚返回,scaffold 已完成(daemon 同步内联跑),等 assistant pnpm install
8687
- "installing",
8688
- // assistant pnpm install 中
8689
- "starting-dev-server",
8690
- // startDevServer 已调,supervisor spawn 子进程中(await ready 期间也在此 stage)
8691
- "running",
8692
- // dev server 跑起来了(supervisor stdout 命中 readyPattern + 端口已监听)
8693
- "stopped",
8694
- // 用户显式点"收起预览"停掉 / daemon 重启后默认;UI 渲染折叠条 + "展开预览"按钮
8695
- "failed"
8696
- // 某个阶段失败(stageReason 字段携带原因,含 dev server cold start timeout)
8697
- ];
8698
- ProjectStageSchema = external_exports.enum(PROJECT_STAGE_VALUES);
8699
- ProjectWithStatusSchema = ProjectMetadataSchema.extend({
8700
- isRunning: external_exports.boolean(),
8701
- boundSessionId: external_exports.string().nullable(),
8702
- /**
8703
- * 单栏 refactor stage fix (spec 2026-06-02 §5.6.2):daemon 内存维护的当前 stage。
8704
- * UI 据此切 PreviewPane spinner / iframe / error。
8705
- * optional 兼容启动后 daemon 还没有 stage 记录的 fresh project(按 isRunning 推断兜底)。
8706
- */
8707
- stage: ProjectStageSchema.optional(),
8708
- /** stage='failed' 时携带失败原因 */
8709
- stageReason: external_exports.string().optional(),
8710
- /**
8711
- * 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.1.3):
8712
- * ProjectMetadata.publishJob 的 status 派生版。daemon 响应时按以下规则实时拼装:
8713
- * - 落盘字段存在 + PublishJobRegistry 有匹配 jobId → 'in-flight'
8714
- * - 落盘字段存在 + Registry 无匹配(jobId 对不上或 entry 缺失)→ 'interrupted'(daemon 崩残骸)
8715
- * - 落盘字段不存在 → undefined(字段省略)
8716
- * UI 用 status 决定 PreviewPane 渲染分支(in-flight 三段 checklist / interrupted 恢复条)。
8717
- */
8718
- publishJob: external_exports.object({
8719
- jobId: external_exports.string().min(1),
8720
- stage: external_exports.enum(["build", "deploy", "verify"]),
8721
- startedAt: external_exports.number().int().nonnegative(),
8722
- status: external_exports.enum(["in-flight", "interrupted"])
8723
- }).optional()
8724
- });
8725
- ListProjectsArgsSchema = external_exports.object({}).strict();
8726
- ListProjectsResultSchema = external_exports.object({
8727
- projects: external_exports.array(ProjectWithStatusSchema)
8728
- }).strict();
8729
- GetProjectArgsSchema = external_exports.object({
8730
- sessionId: external_exports.string().min(1)
8731
- }).strict();
8732
- GetProjectResultSchema = external_exports.object({
8733
- /** session 未绑 project / project 不存在时为 null(UI 走单栏 / 折叠) */
8734
- project: ProjectWithStatusSchema.nullable()
8735
- }).strict();
8736
- CreateProjectArgsSchema = external_exports.object({
8737
- /** 必填:caller session,daemon 自动绑过去(spec 2026-06-02 §3 invariant 1 Session ↔ 1 Project) */
8738
- sessionId: external_exports.string().min(1),
8739
- // .min(1):改造前手写校验是 `!f.name` 明确拒空串;空 name 会让 projectDir('')
8740
- // 落到 projects 根目录,scaffold 往根上铺模板。
8741
- name: external_exports.string().min(1)
8742
- }).strict();
8743
- CreateProjectResultSchema = external_exports.object({
8744
- project: ProjectMetadataSchema
8745
- }).strict();
8746
- DeleteProjectArgsSchema = external_exports.object({
8747
- name: external_exports.string()
8748
- }).strict();
8749
- DeleteProjectResultSchema = external_exports.object({}).strict();
8750
- UpdateProjectPortArgsSchema = external_exports.object({
8751
- name: external_exports.string(),
8752
- newPort: external_exports.number().int()
8753
- }).strict();
8754
- UpdateProjectPortResultSchema = external_exports.object({
8755
- project: ProjectMetadataSchema
8756
- }).strict();
8757
- SetProdUrlArgsSchema = external_exports.object({
8758
- name: external_exports.string().regex(projectNameRegex),
8759
- url: external_exports.string().url()
8760
- }).strict();
8761
- SetProdUrlResultSchema = external_exports.object({
8762
- project: ProjectMetadataSchema
8763
- }).strict();
8764
- StartDevServerArgsSchema = external_exports.object({
8765
- sessionId: external_exports.string().min(1),
8766
- force: external_exports.boolean().optional()
8767
- }).strict();
8768
- StartDevServerResultSchema = external_exports.object({
8769
- project: ProjectMetadataSchema
8770
- }).strict();
8771
- ASSISTANT_REPORTABLE_STAGES = [
8772
- "installing",
8773
- "failed"
8774
- ];
8775
- ReportStageArgsSchema = external_exports.object({
8776
- sessionId: external_exports.string().min(1),
8777
- stage: external_exports.enum(ASSISTANT_REPORTABLE_STAGES),
8778
- /** stage='failed' 时必填的原因(一句话),其它 stage optional */
8779
- reason: external_exports.string().optional()
8780
- }).strict();
8781
- ReportStageResultSchema = external_exports.object({
8782
- ok: external_exports.literal(true)
8783
- }).strict();
8784
- StopDevServerArgsSchema = external_exports.object({
8785
- sessionId: external_exports.string().min(1)
8786
- }).strict();
8787
- StopDevServerResultSchema = external_exports.object({
8788
- ok: external_exports.literal(true)
8789
- }).strict();
8790
- AppBuilderProjectUpdatedEventSchema = external_exports.object({
8791
- type: external_exports.literal("appBuilder:project-updated"),
8792
- project: ProjectWithStatusSchema,
8793
- stage: ProjectStageSchema,
8794
- /** stage='failed' 时的失败原因 */
8795
- stageReason: external_exports.string().optional()
8796
- }).strict();
8797
- PublishArgsSchema = external_exports.object({
8798
- name: external_exports.string().regex(projectNameRegex)
8799
- });
8800
- PublishResultSchema = external_exports.object({
8801
- jobId: external_exports.string().min(1),
8802
- status: external_exports.enum(["started", "already-publishing"])
8803
- }).strict();
8804
- DismissPublishJobArgsSchema = external_exports.object({
8805
- name: external_exports.string().regex(projectNameRegex)
8806
- });
8807
- DismissPublishJobResultSchema = external_exports.object({
8808
- ok: external_exports.literal(true)
8809
- }).strict();
8810
- AppBuilderPublishProgressEventSchema = external_exports.object({
8811
- type: external_exports.literal("appBuilder:publish-progress"),
8812
- name: external_exports.string().regex(projectNameRegex),
8813
- jobId: external_exports.string().min(1),
8814
- stage: external_exports.enum(["build", "deploy", "verify"]),
8815
- status: external_exports.enum(["started", "completed"])
8816
- }).strict();
8817
- AppBuilderPublishFailedEventSchema = external_exports.object({
8818
- type: external_exports.literal("appBuilder:publish-failed"),
8819
- name: external_exports.string().regex(projectNameRegex),
8820
- jobId: external_exports.string().min(1),
8821
- stage: external_exports.enum(["build", "deploy", "verify", "unknown"]),
8822
- errorSummary: external_exports.string()
8823
- }).strict();
8824
8630
  }
8825
8631
  });
8826
8632
 
@@ -8900,7 +8706,7 @@ var init_methods = __esm({
8900
8706
  args: SessionUpdateArgs
8901
8707
  },
8902
8708
  "session:delete": {
8903
- summary: "\u5220\u4F1A\u8BDD\uFF1A\u505C\u8FDB\u7A0B + \u5220 SessionFile\uFF0C\u5E76\u8054\u52A8\u6E05\u7406\u7ED1\u5B9A\u7684 app-builder project",
8709
+ summary: "\u5220\u4F1A\u8BDD\uFF1A\u505C\u8FDB\u7A0B + \u5220 SessionFile",
8904
8710
  args: SessionIdArgs
8905
8711
  },
8906
8712
  "session:send": {
@@ -9491,50 +9297,6 @@ var init_methods = __esm({
9491
9297
  "device:connections": {
9492
9298
  summary: '\u5217\u51FA\u672C daemon \u5F53\u524D\u6240\u6709 owner WS \u8FDE\u63A5\uFF08\u672C\u673A UI / \u624B\u673A / \u7B2C\u4E8C\u53F0\u684C\u9762\uFF09+ \u5404\u81EA clientInfo\u3002owner-only\uFF1Bguest \u8FDE\u63A5\u4E0D\u5728\u5217\uFF08\u4E0D\u662F"\u6211\u7684\u8BBE\u5907"\u4E14\u4E0D\u5411 UI \u6CC4\u9732 guest \u4FE1\u606F\uFF09',
9493
9299
  args: NO_ARGS
9494
- },
9495
- "appBuilder:listProjects": {
9496
- summary: "\u5217\u51FA app-builder project\uFF08\u542B dev server \u72B6\u6001\uFF09",
9497
- args: ListProjectsArgsSchema
9498
- },
9499
- "appBuilder:getProject": {
9500
- summary: "\u6309 sessionId \u53D6\u5355\u4E2A\u7ED1\u5B9A\u7684 project\uFF08\u542B status\uFF09",
9501
- args: GetProjectArgsSchema
9502
- },
9503
- "appBuilder:createProject": {
9504
- summary: "\u7ED9\u4F1A\u8BDD\u7ED1\u5B9A\u65B0 project\uFF1A\u6821\u9A8C\u540D + \u4ECE PROJECT_PORT_MIN..MAX \u6BB5\uFF08\u5F53\u524D 6173-6272\uFF09\u5206\u914D\u672A\u5360\u7528\u7AEF\u53E3\uFF08\u8DE8 user \u5168\u5C40\u552F\u4E00\uFF0C\u6C60\u6EE1\u62A5\u9519\uFF09+ \u5EFA\u76EE\u5F55\u5199\u5143\u6570\u636E + **\u540C\u6B65 scaffold**\uFF08spawn \u811A\u672C\u628A nestjs-react \u6A21\u677F\u62F7\u8FDB\u53BB\uFF1B\u5931\u8D25\u4F1A\u5220\u6389\u521A\u5EFA\u7684\u76EE\u5F55\u518D\u629B INTERNAL\uFF0C\u4E0D\u7559\u534A\u6210\u54C1\uFF09\u3002**\u88C5\u4F9D\u8D56\u548C\u8D77 dev server \u90FD\u4E0D\u5728\u8FD9\u91CC**\uFF1A\u843D\u5730 stage=install-pending\uFF0C\u8C03\u7528\u65B9\u81EA\u5DF1 pnpm install + appBuilder:reportStage + appBuilder:startDevServer",
9505
- args: CreateProjectArgsSchema
9506
- },
9507
- "appBuilder:deleteProject": {
9508
- summary: "\u505C dev server + \u5220 project \u76EE\u5F55\uFF08\u7ED1\u5B9A\u7684\u4F1A\u8BDD\u4E0D\u5728\u8FD9\u91CC\u5220\uFF09",
9509
- args: DeleteProjectArgsSchema
9510
- },
9511
- "appBuilder:updateProjectPort": {
9512
- summary: "\u6539 dev server \u7AEF\u53E3\uFF08\u6821\u9A8C\u5728 PROJECT_PORT_MIN..MAX \u6BB5\u5185\u3001\u5F53\u524D 6173-6272\uFF1B**\u649E\u8F66\u53EA\u67E5\u8C03\u7528\u65B9\u81EA\u5DF1\u540D\u4E0B\u7684 project**\uFF0C\u4E0D\u50CF createProject \u90A3\u6837\u8DE8 user \u67E5\uFF09\uFF1A\u505C\u65E7\u7AEF\u53E3 dev server + \u6539 .clawd-project.json + \u7F6E stage=stopped\u3002**\u4E0D\u81EA\u52A8\u62C9\u8D77\u65B0\u7AEF\u53E3\u7684 dev server**\u2014\u2014\u8981\u8D77\u5F97\u518D\u8C03 appBuilder:startDevServer\uFF08\u90A3\u6761\u624D\u8D70 readiness probe\uFF09",
9513
- args: UpdateProjectPortArgsSchema
9514
- },
9515
- "appBuilder:setProdUrl": {
9516
- summary: "\u53D1\u5E03\u4E0A\u7EBF\u540E\u56DE\u586B\u7EBF\u4E0A\u5730\u5740\u5230 .clawd-project.json",
9517
- args: SetProdUrlArgsSchema
9518
- },
9519
- "appBuilder:startDevServer": {
9520
- summary: "scaffold + \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210\u540E\u663E\u5F0F\u62C9\u8D77 dev server\uFF08createProject \u4E0D\u518D\u81EA\u52A8\u8D77\uFF09",
9521
- args: StartDevServerArgsSchema
9522
- },
9523
- "appBuilder:reportStage": {
9524
- summary: "\u4E0A\u62A5\u811A\u624B\u67B6\u9636\u6BB5\uFF08scaffolding / installing / failed \u7B49\uFF09\uFF0Cdaemon \u5E7F\u64AD\u5E27\u8BA9 UI \u5207 PreviewPane",
9525
- args: ReportStageArgsSchema
9526
- },
9527
- "appBuilder:stopDevServer": {
9528
- summary: "\u6536\u8D77\u9884\u89C8\uFF1A\u505C\u6389 dev server\uFF08\u5BF9\u79F0 startDevServer\uFF09",
9529
- args: StopDevServerArgsSchema
9530
- },
9531
- "appBuilder:publish": {
9532
- summary: "\u53D1\u5E03\u4E0A\u7EBF\uFF1A\u4E32\u884C\u5316 spawn publish.sh \u5E76\u5E7F\u64AD\u9636\u6BB5\u8FDB\u5EA6\u5E27\uFF1B\u540C project \u5DF2\u6709 in-flight job \u65F6\u8FD4\u56DE already-publishing",
9533
- args: PublishArgsSchema
9534
- },
9535
- "appBuilder:dismissPublishJob": {
9536
- summary: "\u53D6\u6D88 in-flight publish\uFF08SIGTERM \u5B50\u8FDB\u7A0B\uFF09\u6216\u6E05\u6389 daemon \u5D29\u6E83\u7559\u4E0B\u7684\u6B8B\u9AB8 publishJob \u5B57\u6BB5",
9537
- args: DismissPublishJobArgsSchema
9538
9300
  }
9539
9301
  };
9540
9302
  METHOD_NAMES = Object.keys(METHOD_DOCS);
@@ -9602,10 +9364,6 @@ var init_errors2 = __esm({
9602
9364
  // daemon 进程身份未激活为飞书 unionId 时,FEISHU_GATED_METHODS 内的 RPC 一律返回此错误;
9603
9365
  // UI 据此显示登录引导(区别于 UNAUTHORIZED 的越权语义)。
9604
9366
  FEISHU_LOGIN_REQUIRED: "FEISHU_LOGIN_REQUIRED",
9605
- // app-builder 单栏默认 refactor (spec 2026-06-02-app-builder-single-pane-default-design):
9606
- // appBuilder:createProject 加 personaId + session 未绑校验后抛的两个错码。
9607
- WRONG_PERSONA: "WRONG_PERSONA",
9608
- SESSION_ALREADY_BOUND: "SESSION_ALREADY_BOUND",
9609
9367
  // 2026-07-02 unified people sidebar: contact:pin 时 deviceId 不在 store(可能被别处删了)
9610
9368
  CONTACT_NOT_FOUND: "CONTACT_NOT_FOUND",
9611
9369
  // 统一 RPC 鉴权(spec 2026-07-28):personaDispatch:get 查不到台账行,或 guest ctx
@@ -11021,8 +10779,8 @@ var require_atomic_sleep = __commonJS({
11021
10779
  var require_sonic_boom = __commonJS({
11022
10780
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
11023
10781
  "use strict";
11024
- var fs92 = require("fs");
11025
- var EventEmitter3 = require("events");
10782
+ var fs90 = require("fs");
10783
+ var EventEmitter2 = require("events");
11026
10784
  var inherits = require("util").inherits;
11027
10785
  var path101 = require("path");
11028
10786
  var sleep2 = require_atomic_sleep();
@@ -11078,20 +10836,20 @@ var require_sonic_boom = __commonJS({
11078
10836
  const mode = sonic.mode;
11079
10837
  if (sonic.sync) {
11080
10838
  try {
11081
- if (sonic.mkdir) fs92.mkdirSync(path101.dirname(file), { recursive: true });
11082
- const fd = fs92.openSync(file, flags, mode);
10839
+ if (sonic.mkdir) fs90.mkdirSync(path101.dirname(file), { recursive: true });
10840
+ const fd = fs90.openSync(file, flags, mode);
11083
10841
  fileOpened(null, fd);
11084
10842
  } catch (err) {
11085
10843
  fileOpened(err);
11086
10844
  throw err;
11087
10845
  }
11088
10846
  } else if (sonic.mkdir) {
11089
- fs92.mkdir(path101.dirname(file), { recursive: true }, (err) => {
10847
+ fs90.mkdir(path101.dirname(file), { recursive: true }, (err) => {
11090
10848
  if (err) return fileOpened(err);
11091
- fs92.open(file, flags, mode, fileOpened);
10849
+ fs90.open(file, flags, mode, fileOpened);
11092
10850
  });
11093
10851
  } else {
11094
- fs92.open(file, flags, mode, fileOpened);
10852
+ fs90.open(file, flags, mode, fileOpened);
11095
10853
  }
11096
10854
  }
11097
10855
  function SonicBoom(opts) {
@@ -11132,8 +10890,8 @@ var require_sonic_boom = __commonJS({
11132
10890
  this.flush = flushBuffer;
11133
10891
  this.flushSync = flushBufferSync;
11134
10892
  this._actualWrite = actualWriteBuffer;
11135
- fsWriteSync = () => fs92.writeSync(this.fd, this._writingBuf);
11136
- fsWrite = () => fs92.write(this.fd, this._writingBuf, this.release);
10893
+ fsWriteSync = () => fs90.writeSync(this.fd, this._writingBuf);
10894
+ fsWrite = () => fs90.write(this.fd, this._writingBuf, this.release);
11137
10895
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
11138
10896
  this._writingBuf = "";
11139
10897
  this.write = write;
@@ -11142,15 +10900,15 @@ var require_sonic_boom = __commonJS({
11142
10900
  this._actualWrite = actualWrite;
11143
10901
  fsWriteSync = () => {
11144
10902
  if (Buffer.isBuffer(this._writingBuf)) {
11145
- return fs92.writeSync(this.fd, this._writingBuf);
10903
+ return fs90.writeSync(this.fd, this._writingBuf);
11146
10904
  }
11147
- return fs92.writeSync(this.fd, this._writingBuf, "utf8");
10905
+ return fs90.writeSync(this.fd, this._writingBuf, "utf8");
11148
10906
  };
11149
10907
  fsWrite = () => {
11150
10908
  if (Buffer.isBuffer(this._writingBuf)) {
11151
- return fs92.write(this.fd, this._writingBuf, this.release);
10909
+ return fs90.write(this.fd, this._writingBuf, this.release);
11152
10910
  }
11153
- return fs92.write(this.fd, this._writingBuf, "utf8", this.release);
10911
+ return fs90.write(this.fd, this._writingBuf, "utf8", this.release);
11154
10912
  };
11155
10913
  } else {
11156
10914
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -11207,7 +10965,7 @@ var require_sonic_boom = __commonJS({
11207
10965
  }
11208
10966
  }
11209
10967
  if (this._fsync) {
11210
- fs92.fsyncSync(this.fd);
10968
+ fs90.fsyncSync(this.fd);
11211
10969
  }
11212
10970
  const len = this._len;
11213
10971
  if (this._reopening) {
@@ -11259,7 +11017,7 @@ var require_sonic_boom = __commonJS({
11259
11017
  sonic._asyncDrainScheduled = false;
11260
11018
  sonic.emit("drain");
11261
11019
  }
11262
- inherits(SonicBoom, EventEmitter3);
11020
+ inherits(SonicBoom, EventEmitter2);
11263
11021
  function mergeBuf(bufs, len) {
11264
11022
  if (bufs.length === 0) {
11265
11023
  return kEmptyBuffer;
@@ -11321,7 +11079,7 @@ var require_sonic_boom = __commonJS({
11321
11079
  const onDrain = () => {
11322
11080
  if (!this._fsync) {
11323
11081
  try {
11324
- fs92.fsync(this.fd, (err) => {
11082
+ fs90.fsync(this.fd, (err) => {
11325
11083
  this._flushPending = false;
11326
11084
  cb(err);
11327
11085
  });
@@ -11423,7 +11181,7 @@ var require_sonic_boom = __commonJS({
11423
11181
  const fd = this.fd;
11424
11182
  this.once("ready", () => {
11425
11183
  if (fd !== this.fd) {
11426
- fs92.close(fd, (err) => {
11184
+ fs90.close(fd, (err) => {
11427
11185
  if (err) {
11428
11186
  return this.emit("error", err);
11429
11187
  }
@@ -11472,7 +11230,7 @@ var require_sonic_boom = __commonJS({
11472
11230
  buf = this._bufs[0];
11473
11231
  }
11474
11232
  try {
11475
- const n = Buffer.isBuffer(buf) ? fs92.writeSync(this.fd, buf) : fs92.writeSync(this.fd, buf, "utf8");
11233
+ const n = Buffer.isBuffer(buf) ? fs90.writeSync(this.fd, buf) : fs90.writeSync(this.fd, buf, "utf8");
11476
11234
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
11477
11235
  buf = releasedBufObj.writingBuf;
11478
11236
  this._len = releasedBufObj.len;
@@ -11488,7 +11246,7 @@ var require_sonic_boom = __commonJS({
11488
11246
  }
11489
11247
  }
11490
11248
  try {
11491
- fs92.fsyncSync(this.fd);
11249
+ fs90.fsyncSync(this.fd);
11492
11250
  } catch {
11493
11251
  }
11494
11252
  }
@@ -11509,7 +11267,7 @@ var require_sonic_boom = __commonJS({
11509
11267
  buf = mergeBuf(this._bufs[0], this._lens[0]);
11510
11268
  }
11511
11269
  try {
11512
- const n = fs92.writeSync(this.fd, buf);
11270
+ const n = fs90.writeSync(this.fd, buf);
11513
11271
  buf = buf.subarray(n);
11514
11272
  this._len = Math.max(this._len - n, 0);
11515
11273
  if (buf.length <= 0) {
@@ -11537,13 +11295,13 @@ var require_sonic_boom = __commonJS({
11537
11295
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
11538
11296
  if (this.sync) {
11539
11297
  try {
11540
- const written = Buffer.isBuffer(this._writingBuf) ? fs92.writeSync(this.fd, this._writingBuf) : fs92.writeSync(this.fd, this._writingBuf, "utf8");
11298
+ const written = Buffer.isBuffer(this._writingBuf) ? fs90.writeSync(this.fd, this._writingBuf) : fs90.writeSync(this.fd, this._writingBuf, "utf8");
11541
11299
  release2(null, written);
11542
11300
  } catch (err) {
11543
11301
  release2(err);
11544
11302
  }
11545
11303
  } else {
11546
- fs92.write(this.fd, this._writingBuf, release2);
11304
+ fs90.write(this.fd, this._writingBuf, release2);
11547
11305
  }
11548
11306
  }
11549
11307
  function actualWriteBuffer() {
@@ -11552,7 +11310,7 @@ var require_sonic_boom = __commonJS({
11552
11310
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
11553
11311
  if (this.sync) {
11554
11312
  try {
11555
- const written = fs92.writeSync(this.fd, this._writingBuf);
11313
+ const written = fs90.writeSync(this.fd, this._writingBuf);
11556
11314
  release2(null, written);
11557
11315
  } catch (err) {
11558
11316
  release2(err);
@@ -11561,7 +11319,7 @@ var require_sonic_boom = __commonJS({
11561
11319
  if (kCopyBuffer) {
11562
11320
  this._writingBuf = Buffer.from(this._writingBuf);
11563
11321
  }
11564
- fs92.write(this.fd, this._writingBuf, release2);
11322
+ fs90.write(this.fd, this._writingBuf, release2);
11565
11323
  }
11566
11324
  }
11567
11325
  function actualClose(sonic) {
@@ -11577,12 +11335,12 @@ var require_sonic_boom = __commonJS({
11577
11335
  sonic._lens = [];
11578
11336
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
11579
11337
  try {
11580
- fs92.fsync(sonic.fd, closeWrapped);
11338
+ fs90.fsync(sonic.fd, closeWrapped);
11581
11339
  } catch {
11582
11340
  }
11583
11341
  function closeWrapped() {
11584
11342
  if (sonic.fd !== 1 && sonic.fd !== 2) {
11585
- fs92.close(sonic.fd, done);
11343
+ fs90.close(sonic.fd, done);
11586
11344
  } else {
11587
11345
  done();
11588
11346
  }
@@ -11837,9 +11595,9 @@ var require_thread_stream = __commonJS({
11837
11595
  "../node_modules/.pnpm/thread-stream@3.1.0/node_modules/thread-stream/index.js"(exports2, module2) {
11838
11596
  "use strict";
11839
11597
  var { version: version2 } = require_package();
11840
- var { EventEmitter: EventEmitter3 } = require("events");
11598
+ var { EventEmitter: EventEmitter2 } = require("events");
11841
11599
  var { Worker } = require("worker_threads");
11842
- var { join: join20 } = require("path");
11600
+ var { join: join17 } = require("path");
11843
11601
  var { pathToFileURL } = require("url");
11844
11602
  var { wait } = require_wait();
11845
11603
  var {
@@ -11875,7 +11633,7 @@ var require_thread_stream = __commonJS({
11875
11633
  function createWorker(stream, opts) {
11876
11634
  const { filename, workerData } = opts;
11877
11635
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
11878
- const toExecute = bundlerOverrides["thread-stream-worker"] || join20(__dirname, "lib", "worker.js");
11636
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join17(__dirname, "lib", "worker.js");
11879
11637
  const worker = new Worker(toExecute, {
11880
11638
  ...opts.workerOpts,
11881
11639
  trackUnmanagedFds: false,
@@ -11993,7 +11751,7 @@ var require_thread_stream = __commonJS({
11993
11751
  stream.worker.off("exit", onWorkerExit);
11994
11752
  destroy(stream, code !== 0 ? new Error("the worker thread exited") : null);
11995
11753
  }
11996
- var ThreadStream = class extends EventEmitter3 {
11754
+ var ThreadStream = class extends EventEmitter2 {
11997
11755
  constructor(opts = {}) {
11998
11756
  super();
11999
11757
  if (opts.bufferSize < 4) {
@@ -12261,7 +12019,7 @@ var require_transport = __commonJS({
12261
12019
  "use strict";
12262
12020
  var { createRequire: createRequire2 } = require("module");
12263
12021
  var getCallers = require_caller();
12264
- var { join: join20, isAbsolute: isAbsolute4, sep: sep4 } = require("path");
12022
+ var { join: join17, isAbsolute: isAbsolute4, sep: sep4 } = require("path");
12265
12023
  var sleep2 = require_atomic_sleep();
12266
12024
  var onExit = require_on_exit_leak_free();
12267
12025
  var ThreadStream = require_thread_stream();
@@ -12324,7 +12082,7 @@ var require_transport = __commonJS({
12324
12082
  throw new Error("only one of target or targets can be specified");
12325
12083
  }
12326
12084
  if (targets) {
12327
- target = bundlerOverrides["pino-worker"] || join20(__dirname, "worker.js");
12085
+ target = bundlerOverrides["pino-worker"] || join17(__dirname, "worker.js");
12328
12086
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
12329
12087
  return {
12330
12088
  ...dest,
@@ -12342,7 +12100,7 @@ var require_transport = __commonJS({
12342
12100
  });
12343
12101
  });
12344
12102
  } else if (pipeline3) {
12345
- target = bundlerOverrides["pino-worker"] || join20(__dirname, "worker.js");
12103
+ target = bundlerOverrides["pino-worker"] || join17(__dirname, "worker.js");
12346
12104
  options.pipelines = [pipeline3.map((dest) => {
12347
12105
  return {
12348
12106
  ...dest,
@@ -12364,7 +12122,7 @@ var require_transport = __commonJS({
12364
12122
  return origin;
12365
12123
  }
12366
12124
  if (origin === "pino/file") {
12367
- return join20(__dirname, "..", "file.js");
12125
+ return join17(__dirname, "..", "file.js");
12368
12126
  }
12369
12127
  let fixTarget2;
12370
12128
  for (const filePath of callers) {
@@ -12951,7 +12709,7 @@ var require_meta = __commonJS({
12951
12709
  var require_proto = __commonJS({
12952
12710
  "../node_modules/.pnpm/pino@9.14.0/node_modules/pino/lib/proto.js"(exports2, module2) {
12953
12711
  "use strict";
12954
- var { EventEmitter: EventEmitter3 } = require("events");
12712
+ var { EventEmitter: EventEmitter2 } = require("events");
12955
12713
  var {
12956
12714
  lsCacheSym,
12957
12715
  levelValSym,
@@ -13033,7 +12791,7 @@ var require_proto = __commonJS({
13033
12791
  [getLevelSym]: getLevel,
13034
12792
  [setLevelSym]: setLevel
13035
12793
  };
13036
- Object.setPrototypeOf(prototype, EventEmitter3.prototype);
12794
+ Object.setPrototypeOf(prototype, EventEmitter2.prototype);
13037
12795
  module2.exports = function() {
13038
12796
  return Object.create(prototype);
13039
12797
  };
@@ -13354,7 +13112,7 @@ var require_safe_stable_stringify = __commonJS({
13354
13112
  return circularValue;
13355
13113
  }
13356
13114
  let res = "";
13357
- let join20 = ",";
13115
+ let join17 = ",";
13358
13116
  const originalIndentation = indentation;
13359
13117
  if (Array.isArray(value)) {
13360
13118
  if (value.length === 0) {
@@ -13368,7 +13126,7 @@ var require_safe_stable_stringify = __commonJS({
13368
13126
  indentation += spacer;
13369
13127
  res += `
13370
13128
  ${indentation}`;
13371
- join20 = `,
13129
+ join17 = `,
13372
13130
  ${indentation}`;
13373
13131
  }
13374
13132
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -13376,13 +13134,13 @@ ${indentation}`;
13376
13134
  for (; i < maximumValuesToStringify - 1; i++) {
13377
13135
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
13378
13136
  res += tmp2 !== void 0 ? tmp2 : "null";
13379
- res += join20;
13137
+ res += join17;
13380
13138
  }
13381
13139
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
13382
13140
  res += tmp !== void 0 ? tmp : "null";
13383
13141
  if (value.length - 1 > maximumBreadth) {
13384
13142
  const removedKeys = value.length - maximumBreadth - 1;
13385
- res += `${join20}"... ${getItemCount(removedKeys)} not stringified"`;
13143
+ res += `${join17}"... ${getItemCount(removedKeys)} not stringified"`;
13386
13144
  }
13387
13145
  if (spacer !== "") {
13388
13146
  res += `
@@ -13403,7 +13161,7 @@ ${originalIndentation}`;
13403
13161
  let separator = "";
13404
13162
  if (spacer !== "") {
13405
13163
  indentation += spacer;
13406
- join20 = `,
13164
+ join17 = `,
13407
13165
  ${indentation}`;
13408
13166
  whitespace = " ";
13409
13167
  }
@@ -13417,13 +13175,13 @@ ${indentation}`;
13417
13175
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
13418
13176
  if (tmp !== void 0) {
13419
13177
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
13420
- separator = join20;
13178
+ separator = join17;
13421
13179
  }
13422
13180
  }
13423
13181
  if (keyLength > maximumBreadth) {
13424
13182
  const removedKeys = keyLength - maximumBreadth;
13425
13183
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
13426
- separator = join20;
13184
+ separator = join17;
13427
13185
  }
13428
13186
  if (spacer !== "" && separator.length > 1) {
13429
13187
  res = `
@@ -13464,7 +13222,7 @@ ${originalIndentation}`;
13464
13222
  }
13465
13223
  const originalIndentation = indentation;
13466
13224
  let res = "";
13467
- let join20 = ",";
13225
+ let join17 = ",";
13468
13226
  if (Array.isArray(value)) {
13469
13227
  if (value.length === 0) {
13470
13228
  return "[]";
@@ -13477,7 +13235,7 @@ ${originalIndentation}`;
13477
13235
  indentation += spacer;
13478
13236
  res += `
13479
13237
  ${indentation}`;
13480
- join20 = `,
13238
+ join17 = `,
13481
13239
  ${indentation}`;
13482
13240
  }
13483
13241
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -13485,13 +13243,13 @@ ${indentation}`;
13485
13243
  for (; i < maximumValuesToStringify - 1; i++) {
13486
13244
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
13487
13245
  res += tmp2 !== void 0 ? tmp2 : "null";
13488
- res += join20;
13246
+ res += join17;
13489
13247
  }
13490
13248
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
13491
13249
  res += tmp !== void 0 ? tmp : "null";
13492
13250
  if (value.length - 1 > maximumBreadth) {
13493
13251
  const removedKeys = value.length - maximumBreadth - 1;
13494
- res += `${join20}"... ${getItemCount(removedKeys)} not stringified"`;
13252
+ res += `${join17}"... ${getItemCount(removedKeys)} not stringified"`;
13495
13253
  }
13496
13254
  if (spacer !== "") {
13497
13255
  res += `
@@ -13504,7 +13262,7 @@ ${originalIndentation}`;
13504
13262
  let whitespace = "";
13505
13263
  if (spacer !== "") {
13506
13264
  indentation += spacer;
13507
- join20 = `,
13265
+ join17 = `,
13508
13266
  ${indentation}`;
13509
13267
  whitespace = " ";
13510
13268
  }
@@ -13513,7 +13271,7 @@ ${indentation}`;
13513
13271
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
13514
13272
  if (tmp !== void 0) {
13515
13273
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
13516
- separator = join20;
13274
+ separator = join17;
13517
13275
  }
13518
13276
  }
13519
13277
  if (spacer !== "" && separator.length > 1) {
@@ -13571,20 +13329,20 @@ ${originalIndentation}`;
13571
13329
  indentation += spacer;
13572
13330
  let res2 = `
13573
13331
  ${indentation}`;
13574
- const join21 = `,
13332
+ const join18 = `,
13575
13333
  ${indentation}`;
13576
13334
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
13577
13335
  let i = 0;
13578
13336
  for (; i < maximumValuesToStringify - 1; i++) {
13579
13337
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
13580
13338
  res2 += tmp2 !== void 0 ? tmp2 : "null";
13581
- res2 += join21;
13339
+ res2 += join18;
13582
13340
  }
13583
13341
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
13584
13342
  res2 += tmp !== void 0 ? tmp : "null";
13585
13343
  if (value.length - 1 > maximumBreadth) {
13586
13344
  const removedKeys = value.length - maximumBreadth - 1;
13587
- res2 += `${join21}"... ${getItemCount(removedKeys)} not stringified"`;
13345
+ res2 += `${join18}"... ${getItemCount(removedKeys)} not stringified"`;
13588
13346
  }
13589
13347
  res2 += `
13590
13348
  ${originalIndentation}`;
@@ -13600,16 +13358,16 @@ ${originalIndentation}`;
13600
13358
  return '"[Object]"';
13601
13359
  }
13602
13360
  indentation += spacer;
13603
- const join20 = `,
13361
+ const join17 = `,
13604
13362
  ${indentation}`;
13605
13363
  let res = "";
13606
13364
  let separator = "";
13607
13365
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
13608
13366
  if (isTypedArrayWithEntries(value)) {
13609
- res += stringifyTypedArray(value, join20, maximumBreadth);
13367
+ res += stringifyTypedArray(value, join17, maximumBreadth);
13610
13368
  keys = keys.slice(value.length);
13611
13369
  maximumPropertiesToStringify -= value.length;
13612
- separator = join20;
13370
+ separator = join17;
13613
13371
  }
13614
13372
  if (deterministic) {
13615
13373
  keys = sort(keys, comparator);
@@ -13620,13 +13378,13 @@ ${indentation}`;
13620
13378
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
13621
13379
  if (tmp !== void 0) {
13622
13380
  res += `${separator}${strEscape(key2)}: ${tmp}`;
13623
- separator = join20;
13381
+ separator = join17;
13624
13382
  }
13625
13383
  }
13626
13384
  if (keyLength > maximumBreadth) {
13627
13385
  const removedKeys = keyLength - maximumBreadth;
13628
13386
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
13629
- separator = join20;
13387
+ separator = join17;
13630
13388
  }
13631
13389
  if (separator !== "") {
13632
13390
  res = `
@@ -32864,10 +32622,10 @@ var require_extension = __commonJS({
32864
32622
  var require_websocket = __commonJS({
32865
32623
  "../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket.js"(exports2, module2) {
32866
32624
  "use strict";
32867
- var EventEmitter3 = require("events");
32625
+ var EventEmitter2 = require("events");
32868
32626
  var https = require("https");
32869
- var http3 = require("http");
32870
- var net3 = require("net");
32627
+ var http2 = require("http");
32628
+ var net2 = require("net");
32871
32629
  var tls = require("tls");
32872
32630
  var { randomBytes, createHash: createHash2 } = require("crypto");
32873
32631
  var { Duplex, Readable: Readable3 } = require("stream");
@@ -32896,7 +32654,7 @@ var require_websocket = __commonJS({
32896
32654
  var protocolVersions = [8, 13];
32897
32655
  var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
32898
32656
  var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
32899
- var WebSocket2 = class _WebSocket extends EventEmitter3 {
32657
+ var WebSocket2 = class _WebSocket extends EventEmitter2 {
32900
32658
  /**
32901
32659
  * Create a new `WebSocket`.
32902
32660
  *
@@ -33400,7 +33158,7 @@ var require_websocket = __commonJS({
33400
33158
  }
33401
33159
  const defaultPort = isSecure ? 443 : 80;
33402
33160
  const key = randomBytes(16).toString("base64");
33403
- const request = isSecure ? https.request : http3.request;
33161
+ const request = isSecure ? https.request : http2.request;
33404
33162
  const protocolSet = /* @__PURE__ */ new Set();
33405
33163
  let perMessageDeflate;
33406
33164
  opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
@@ -33601,12 +33359,12 @@ var require_websocket = __commonJS({
33601
33359
  }
33602
33360
  function netConnect(options) {
33603
33361
  options.path = options.socketPath;
33604
- return net3.connect(options);
33362
+ return net2.connect(options);
33605
33363
  }
33606
33364
  function tlsConnect(options) {
33607
33365
  options.path = void 0;
33608
33366
  if (!options.servername && options.servername !== "") {
33609
- options.servername = net3.isIP(options.host) ? "" : options.host;
33367
+ options.servername = net2.isIP(options.host) ? "" : options.host;
33610
33368
  }
33611
33369
  return tls.connect(options);
33612
33370
  }
@@ -33893,8 +33651,8 @@ var require_subprotocol = __commonJS({
33893
33651
  var require_websocket_server = __commonJS({
33894
33652
  "../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket-server.js"(exports2, module2) {
33895
33653
  "use strict";
33896
- var EventEmitter3 = require("events");
33897
- var http3 = require("http");
33654
+ var EventEmitter2 = require("events");
33655
+ var http2 = require("http");
33898
33656
  var { Duplex } = require("stream");
33899
33657
  var { createHash: createHash2 } = require("crypto");
33900
33658
  var extension2 = require_extension();
@@ -33906,7 +33664,7 @@ var require_websocket_server = __commonJS({
33906
33664
  var RUNNING = 0;
33907
33665
  var CLOSING = 1;
33908
33666
  var CLOSED = 2;
33909
- var WebSocketServer2 = class extends EventEmitter3 {
33667
+ var WebSocketServer2 = class extends EventEmitter2 {
33910
33668
  /**
33911
33669
  * Create a `WebSocketServer` instance.
33912
33670
  *
@@ -33969,8 +33727,8 @@ var require_websocket_server = __commonJS({
33969
33727
  );
33970
33728
  }
33971
33729
  if (options.port != null) {
33972
- this._server = http3.createServer((req, res) => {
33973
- const body = http3.STATUS_CODES[426];
33730
+ this._server = http2.createServer((req, res) => {
33731
+ const body = http2.STATUS_CODES[426];
33974
33732
  res.writeHead(426, {
33975
33733
  "Content-Length": body.length,
33976
33734
  "Content-Type": "text/plain"
@@ -34257,7 +34015,7 @@ var require_websocket_server = __commonJS({
34257
34015
  this.destroy();
34258
34016
  }
34259
34017
  function abortHandshake(socket, code, message, headers) {
34260
- message = message || http3.STATUS_CODES[code];
34018
+ message = message || http2.STATUS_CODES[code];
34261
34019
  headers = {
34262
34020
  Connection: "close",
34263
34021
  "Content-Type": "text/html",
@@ -34266,7 +34024,7 @@ var require_websocket_server = __commonJS({
34266
34024
  };
34267
34025
  socket.once("finish", socket.destroy);
34268
34026
  socket.end(
34269
- `HTTP/1.1 ${code} ${http3.STATUS_CODES[code]}\r
34027
+ `HTTP/1.1 ${code} ${http2.STATUS_CODES[code]}\r
34270
34028
  ` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
34271
34029
  );
34272
34030
  }
@@ -34569,7 +34327,7 @@ var require_BufferList = __commonJS({
34569
34327
  this.head = this.tail = null;
34570
34328
  this.length = 0;
34571
34329
  };
34572
- BufferList.prototype.join = function join20(s) {
34330
+ BufferList.prototype.join = function join17(s) {
34573
34331
  if (this.length === 0) return "";
34574
34332
  var p2 = this.head;
34575
34333
  var ret = "" + p2.data;
@@ -44026,7 +43784,7 @@ var require_lib3 = __commonJS({
44026
43784
  // src/run-case/recorder.ts
44027
43785
  function startRunCaseRecorder(opts) {
44028
43786
  const now = opts.now ?? Date.now;
44029
- const dir = import_node_path83.default.dirname(opts.recordPath);
43787
+ const dir = import_node_path80.default.dirname(opts.recordPath);
44030
43788
  let stream = null;
44031
43789
  let closing = false;
44032
43790
  let closedSettled = false;
@@ -44040,8 +43798,8 @@ function startRunCaseRecorder(opts) {
44040
43798
  });
44041
43799
  const ensureStream = () => {
44042
43800
  if (stream) return stream;
44043
- import_node_fs64.default.mkdirSync(dir, { recursive: true });
44044
- stream = import_node_fs64.default.createWriteStream(opts.recordPath, { flags: "a" });
43801
+ import_node_fs61.default.mkdirSync(dir, { recursive: true });
43802
+ stream = import_node_fs61.default.createWriteStream(opts.recordPath, { flags: "a" });
44045
43803
  stream.on("close", () => closedResolve());
44046
43804
  return stream;
44047
43805
  };
@@ -44066,12 +43824,12 @@ function startRunCaseRecorder(opts) {
44066
43824
  };
44067
43825
  return { tap, close, closed };
44068
43826
  }
44069
- var import_node_fs64, import_node_path83;
43827
+ var import_node_fs61, import_node_path80;
44070
43828
  var init_recorder = __esm({
44071
43829
  "src/run-case/recorder.ts"() {
44072
43830
  "use strict";
44073
- import_node_fs64 = __toESM(require("fs"), 1);
44074
- import_node_path83 = __toESM(require("path"), 1);
43831
+ import_node_fs61 = __toESM(require("fs"), 1);
43832
+ import_node_path80 = __toESM(require("path"), 1);
44075
43833
  }
44076
43834
  });
44077
43835
 
@@ -44114,7 +43872,7 @@ var init_wire = __esm({
44114
43872
  // src/run-case/controller.ts
44115
43873
  async function runController(opts) {
44116
43874
  const now = opts.now ?? Date.now;
44117
- const cwd = opts.cwd ?? (0, import_node_fs65.mkdtempSync)(import_node_path84.default.join(import_node_os29.default.tmpdir(), "clawd-runcase-"));
43875
+ const cwd = opts.cwd ?? (0, import_node_fs62.mkdtempSync)(import_node_path81.default.join(import_node_os29.default.tmpdir(), "clawd-runcase-"));
44118
43876
  const ownsCwd = opts.cwd === void 0;
44119
43877
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
44120
43878
  const spawnCtx = { cwd };
@@ -44275,19 +44033,19 @@ async function runController(opts) {
44275
44033
  if (sigintHandler) process.off("SIGINT", sigintHandler);
44276
44034
  if (ownsCwd) {
44277
44035
  try {
44278
- (0, import_node_fs65.rmSync)(cwd, { recursive: true, force: true });
44036
+ (0, import_node_fs62.rmSync)(cwd, { recursive: true, force: true });
44279
44037
  } catch {
44280
44038
  }
44281
44039
  }
44282
44040
  return exitCode ?? 0;
44283
44041
  }
44284
- var import_node_fs65, import_node_os29, import_node_path84;
44042
+ var import_node_fs62, import_node_os29, import_node_path81;
44285
44043
  var init_controller = __esm({
44286
44044
  "src/run-case/controller.ts"() {
44287
44045
  "use strict";
44288
- import_node_fs65 = require("fs");
44046
+ import_node_fs62 = require("fs");
44289
44047
  import_node_os29 = __toESM(require("os"), 1);
44290
- import_node_path84 = __toESM(require("path"), 1);
44048
+ import_node_path81 = __toESM(require("path"), 1);
44291
44049
  init_claude();
44292
44050
  init_stdout_splitter();
44293
44051
  init_permission_stdio();
@@ -44507,8 +44265,6 @@ function resolveConfig(opts) {
44507
44265
  const frpcBinary = env.CLAWD_FRPC_BIN ?? null;
44508
44266
  const rawMode = env.CLAWD_CC_MODE;
44509
44267
  const mode = DAEMON_MODE_VALUES.includes(rawMode ?? "") ? rawMode : "sdk";
44510
- const filePreviewPorts = Array.isArray(fileCfg.previewPorts) ? fileCfg.previewPorts.map((n) => Number(n)).filter((n) => Number.isInteger(n) && n > 0) : null;
44511
- const previewPorts = env.CLAWD_PREVIEW_PORTS ? env.CLAWD_PREVIEW_PORTS.split(",").map((s) => Number(s.trim())).filter((n) => Number.isInteger(n) && n > 0) : filePreviewPorts && filePreviewPorts.length > 0 ? filePreviewPorts : null;
44512
44268
  const ownerIdleKillDelayMs = resolveIdleKillMs(env.CLAWD_OWNER_IDLE_KILL_DELAY_MS) ?? resolveIdleKillMs(fileCfg.ownerIdleKillDelayMs) ?? DEFAULT_OWNER_IDLE_KILL_DELAY_MS;
44513
44269
  const logShipping = resolveLogShipping(
44514
44270
  fileCfg.logShipping,
@@ -44526,7 +44282,6 @@ function resolveConfig(opts) {
44526
44282
  noTunnelPersist,
44527
44283
  frpcBinary,
44528
44284
  mode,
44529
- previewPorts,
44530
44285
  logShipping,
44531
44286
  ownerIdleKillDelayMs
44532
44287
  };
@@ -44562,10 +44317,10 @@ Env (advanced):
44562
44317
  `;
44563
44318
 
44564
44319
  // src/index.ts
44565
- var import_node_path82 = __toESM(require("path"), 1);
44566
- var import_node_fs63 = __toESM(require("fs"), 1);
44320
+ var import_node_path79 = __toESM(require("path"), 1);
44321
+ var import_node_fs60 = __toESM(require("fs"), 1);
44567
44322
  var import_node_os28 = __toESM(require("os"), 1);
44568
- var import_node_crypto23 = require("crypto");
44323
+ var import_node_crypto22 = require("crypto");
44569
44324
  var import_node_url5 = require("url");
44570
44325
 
44571
44326
  // src/logger.ts
@@ -45871,6 +45626,8 @@ function buildSpawnContext(state, deps) {
45871
45626
  if (dispatchId) env.CLAWD_DISPATCH_ID = dispatchId;
45872
45627
  const personaId2 = file.ownerPersonaId;
45873
45628
  if (personaId2) env.CLAWD_PERSONA_ID = personaId2;
45629
+ const userWorkDir = state.subSessionMeta?.userWorkDir;
45630
+ if (userWorkDir) env.CLAWD_APP_BUILDER_ROOT = userWorkDir;
45874
45631
  const mcpConfigs = (deps.getMcpConfigs?.() ?? []).filter(
45875
45632
  (c) => !c.personas || personaId2 != null && c.personas.includes(personaId2)
45876
45633
  );
@@ -47651,14 +47408,6 @@ var SessionManager = class {
47651
47408
  // guest sessions 由 handlers/session.ts list 处按 ctx.principal.kind === 'guest' 做权限过滤
47652
47409
  // (creatorPrincipalId === capId), 这里仅负责数据源汇总.
47653
47410
  // listener sub-session 不走这条(listener 走 persona:listSubSessions RPC 单独入口)。
47654
- /**
47655
- * 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.2.2):
47656
- * 反查绑定到指定 app-builder project 的 session。按 invariant "1 Session ↔ 1 Project"
47657
- * 应当唯一;找不到 → undefined(项目已成孤儿)。
47658
- */
47659
- findOwnedSessionByAppBuilderProject(projectName) {
47660
- return this.listAllOwned().find((file) => file.appBuilderProject === projectName);
47661
- }
47662
47411
  listAllOwned() {
47663
47412
  const out = [];
47664
47413
  out.push(...this.deps.store.list());
@@ -47967,7 +47716,6 @@ var SessionManager = class {
47967
47716
  // projectPath 只在 persona+project 组合写入(spec 2026-08-17-projects-sidebar §3.2);
47968
47717
  // project-only 会话 cwd 即 project 路径,靠 UI 前缀匹配归组
47969
47718
  ...args.ownerPersonaId && args.projectPath ? { projectPath: args.projectPath } : {},
47970
- appBuilderProject: args.appBuilderProject,
47971
47719
  ephemeral: args.ephemeral,
47972
47720
  creatorPrincipalId,
47973
47721
  creatorDisplayName,
@@ -55957,18 +55705,22 @@ async function writeTemplates(dir) {
55957
55705
  await import_promises5.default.mkdir(import_node_path34.default.dirname(wf), { recursive: true });
55958
55706
  await import_promises5.default.writeFile(wf, WORKFLOW_TEMPLATE);
55959
55707
  }
55960
- async function ensureRepo(exec, args) {
55961
- const { dir, org, personaId: personaId2 } = args;
55962
- const existing = await repoInfo(exec, dir);
55963
- if (existing) return { created: false, url: existing.url };
55708
+ var RepoAlreadyExistsError = class extends RepoError {
55709
+ constructor(slug) {
55710
+ super(`\u7EC4\u7EC7\u91CC\u5DF2\u7ECF\u6709 https://github.com/${slug} \u4E86`);
55711
+ this.slug = slug;
55712
+ }
55713
+ slug;
55714
+ };
55715
+ async function createOrgRepo(exec, args) {
55716
+ const { dir, org, repoName, message } = args;
55964
55717
  if (!await isToplevel(exec, dir)) {
55965
55718
  await mustGit(exec, dir, ["init", "-b", "main"], "\u521D\u59CB\u5316\u4ED3\u5E93");
55966
55719
  }
55967
- await writeTemplates(dir);
55968
55720
  const identity = await identityArgs(exec, dir);
55969
55721
  await mustGit(exec, dir, ["add", "-A"], "\u6682\u5B58\u6587\u4EF6");
55970
- await mustGit(exec, dir, [...identity, "commit", "--allow-empty", "-m", "clawd: \u4E0A\u4E91"], "\u9996\u6B21\u63D0\u4EA4");
55971
- const slug = `${org}/${personaId2}`;
55722
+ await mustGit(exec, dir, [...identity, "commit", "--allow-empty", "-m", message], "\u9996\u6B21\u63D0\u4EA4");
55723
+ const slug = `${org}/${repoName}`;
55972
55724
  const r = await exec(
55973
55725
  "gh",
55974
55726
  ["repo", "create", slug, "--private", "--source", dir, "--remote", "origin", "--push"],
@@ -55977,16 +55729,30 @@ async function ensureRepo(exec, args) {
55977
55729
  if (r.spawnError) throw new RepoError(`\u672C\u673A\u8DD1\u4E0D\u4E86 gh\uFF1A${r.spawnError}`);
55978
55730
  if (r.code !== 0) {
55979
55731
  const err = tail(r.stderr) || tail(r.stdout);
55980
- if (/already exists/i.test(err)) {
55732
+ if (/already exists/i.test(err)) throw new RepoAlreadyExistsError(slug);
55733
+ throw new RepoError(`\u5EFA\u4ED3\u5E93\u6CA1\u6210\uFF08gh repo create \u9000\u51FA\u7801 ${r.code}\uFF09\uFF1A${err}`);
55734
+ }
55735
+ const info = await repoInfo(exec, dir);
55736
+ return { slug, url: info?.url ?? `https://github.com/${slug}` };
55737
+ }
55738
+ async function ensureRepo(exec, args) {
55739
+ const { dir, org, personaId: personaId2 } = args;
55740
+ const existing = await repoInfo(exec, dir);
55741
+ if (existing) return { created: false, url: existing.url };
55742
+ await writeTemplates(dir);
55743
+ let created;
55744
+ try {
55745
+ created = await createOrgRepo(exec, { dir, org, repoName: personaId2, message: "clawd: \u4E0A\u4E91" });
55746
+ } catch (err) {
55747
+ if (err instanceof RepoAlreadyExistsError) {
55981
55748
  throw new RepoError(
55982
- `\u7EC4\u7EC7\u91CC\u5DF2\u7ECF\u6709 https://github.com/${slug} \u4E86\uFF0C\u591A\u534A\u662F\u522B\u4EBA\u5DF2\u7ECF\u53D1\u8FC7\u8FD9\u4E2A persona\u3002\u4E00\u4E2A persona \u53EA\u8BE5\u6709\u4E00\u4E2A\u4ED3\u5E93\uFF1A\u8981\u63A5\u7740\u7528\u90A3\u4E2A\u4ED3\u5E93\u7684\u8BDD\uFF0C\u628A\u672C\u5730\u76EE\u5F55\u6362\u6210\u5B83\u7684 clone\u3002`
55749
+ `\u7EC4\u7EC7\u91CC\u5DF2\u7ECF\u6709 https://github.com/${err.slug} \u4E86\uFF0C\u591A\u534A\u662F\u522B\u4EBA\u5DF2\u7ECF\u53D1\u8FC7\u8FD9\u4E2A persona\u3002\u4E00\u4E2A persona \u53EA\u8BE5\u6709\u4E00\u4E2A\u4ED3\u5E93\uFF1A\u8981\u63A5\u7740\u7528\u90A3\u4E2A\u4ED3\u5E93\u7684\u8BDD\uFF0C\u628A\u672C\u5730\u76EE\u5F55\u6362\u6210\u5B83\u7684 clone\u3002`
55983
55750
  );
55984
55751
  }
55985
- throw new RepoError(`\u5EFA\u4ED3\u5E93\u6CA1\u6210\uFF08gh repo create \u9000\u51FA\u7801 ${r.code}\uFF09\uFF1A${err}`);
55752
+ throw err;
55986
55753
  }
55987
- await enableActions(exec, slug);
55988
- const info = await repoInfo(exec, dir);
55989
- return { created: true, url: info?.url ?? `https://github.com/${slug}` };
55754
+ await enableActions(exec, created.slug);
55755
+ return { created: true, url: created.url };
55990
55756
  }
55991
55757
  async function enableActions(exec, slug) {
55992
55758
  await exec(
@@ -56154,7 +55920,7 @@ var SCANNABLE_ASSETS = [
56154
55920
  },
56155
55921
  {
56156
55922
  name: "app-builder-plugin",
56157
- what: "clawd-app-builder MCP server \u7684\u6784\u5EFA\u4EA7\u7269\uFF08daemon \u7684 tsup \u4EA7\u7269\uFF0C\u672C\u673A\u4E0D\u5728\u76D8\u4E0A\u5355\u72EC\u5B58\u4E00\u4EFD\uFF09\u3002**\u5224\u636E**\uFF1Apersona \u7684\u4E3B\u5E72\u52A8\u4F5C\u8D70\u7684\u662F daemon \u63D0\u4F9B\u7684 MCP\uFF08clawd-rpc / app-builder \u90A3\u7C7B\uFF09\u65F6\uFF0C\u4E91\u4E0A\u5BB9\u5668\u91CC\u6CA1\u6709 daemon\uFF0C\u8DD1\u7684\u5C31\u662F\u8FD9\u4EFD\u4EA7\u7269\u2014\u2014\u7528\u5230\u5B83\u5C31\u62A5\u4E00\u6761 asset\uFF0C\u5426\u5219\u5B83\u8FDB\u4E0D\u4E86\u955C\u50CF\u3002"
55923
+ what: "clawd-app-builder MCP server \u7684\u6784\u5EFA\u4EA7\u7269\uFF08daemon \u7684 tsup \u4EA7\u7269\uFF0C\u672C\u673A\u4E0D\u5728\u76D8\u4E0A\u5355\u72EC\u5B58\u4E00\u4EFD\uFF09\u3002**\u5224\u636E**\uFF1Apersona \u7684\u4E3B\u5E72\u52A8\u4F5C\u8D70 clawd-app-builder \u8FD9\u4E2A MCP \u65F6\uFF0C\u4E91\u4E0A\u5BB9\u5668\u91CC\u8DD1\u7684\u5C31\u662F\u8FD9\u4EFD\u4EA7\u7269\u2014\u2014\u7528\u5230\u5B83\u5C31\u62A5\u4E00\u6761 asset\uFF0C\u5426\u5219\u5B83\u8FDB\u4E0D\u4E86\u955C\u50CF\u3002"
56158
55924
  }
56159
55925
  ];
56160
55926
  function staleScannerAssets(scan, known = SCANNABLE_ASSETS) {
@@ -60954,59 +60720,8 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
60954
60720
  var wrapper_default = import_websocket.default;
60955
60721
 
60956
60722
  // src/transport/local-ws-server.ts
60957
- var import_node_http2 = __toESM(require("http"), 1);
60958
- var import_node_crypto10 = require("crypto");
60959
-
60960
- // src/transport/preview-proxy.ts
60961
60723
  var import_node_http = __toESM(require("http"), 1);
60962
- var import_node_net = __toESM(require("net"), 1);
60963
- var PREVIEW_RE = /^\/preview\/(\d+)(?:\/.*)?$/;
60964
- function matchPreviewPort(pathname) {
60965
- const m2 = pathname.match(PREVIEW_RE);
60966
- return m2 ? Number(m2[1]) : null;
60967
- }
60968
- function isPreviewPortAllowed(port, cfg) {
60969
- return cfg.allowedPorts.includes(port);
60970
- }
60971
- function proxyPreviewHttp(req, res, port) {
60972
- return new Promise((resolve9) => {
60973
- const upstream = import_node_http.default.request(
60974
- { host: "localhost", port, method: req.method, path: req.url, headers: req.headers },
60975
- (upRes) => {
60976
- res.writeHead(upRes.statusCode || 502, upRes.headers);
60977
- upRes.pipe(res);
60978
- upRes.on("end", () => resolve9());
60979
- }
60980
- );
60981
- upstream.on("error", (e) => {
60982
- if (!res.headersSent) {
60983
- res.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
60984
- }
60985
- res.end(`preview upstream error: ${e.message}`);
60986
- resolve9();
60987
- });
60988
- req.pipe(upstream);
60989
- });
60990
- }
60991
- function proxyPreviewUpgrade(req, socket, head, port) {
60992
- const upstream = import_node_net.default.connect(port, "localhost", () => {
60993
- let raw = `${req.method} ${req.url} HTTP/1.1\r
60994
- `;
60995
- for (let i = 0; i < req.rawHeaders.length; i += 2) {
60996
- raw += `${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r
60997
- `;
60998
- }
60999
- raw += "\r\n";
61000
- upstream.write(raw);
61001
- if (head && head.length) upstream.write(head);
61002
- upstream.pipe(socket);
61003
- socket.pipe(upstream);
61004
- });
61005
- upstream.on("error", () => socket.destroy());
61006
- socket.on("error", () => upstream.destroy());
61007
- }
61008
-
61009
- // src/transport/local-ws-server.ts
60724
+ var import_node_crypto10 = require("crypto");
61010
60725
  var LocalWsServer = class {
61011
60726
  constructor(opts) {
61012
60727
  this.opts = opts;
@@ -61027,25 +60742,9 @@ var LocalWsServer = class {
61027
60742
  async start() {
61028
60743
  const host = this.opts.host ?? "127.0.0.1";
61029
60744
  await new Promise((resolve9, reject) => {
61030
- const httpServer = import_node_http2.default.createServer((req, res) => this.handleHttpRequest(req, res));
60745
+ const httpServer = import_node_http.default.createServer((req, res) => this.handleHttpRequest(req, res));
61031
60746
  const wss = new import_websocket_server.default({ noServer: true, clientTracking: true });
61032
60747
  httpServer.on("upgrade", (req, socket, head) => {
61033
- if (req.url?.startsWith("/preview/")) {
61034
- const pathname = (() => {
61035
- try {
61036
- return new URL(req.url, "http://localhost").pathname;
61037
- } catch {
61038
- return "/";
61039
- }
61040
- })();
61041
- const port = matchPreviewPort(pathname);
61042
- if (port != null && (this.opts.previewPorts ?? []).includes(port)) {
61043
- proxyPreviewUpgrade(req, socket, head, port);
61044
- } else {
61045
- socket.destroy();
61046
- }
61047
- return;
61048
- }
61049
60748
  wss.handleUpgrade(req, socket, head, (ws) => {
61050
60749
  this.routeConnection(ws, req);
61051
60750
  });
@@ -61129,7 +60828,7 @@ var LocalWsServer = class {
61129
60828
  }
61130
60829
  }
61131
60830
  // 仅广播给 owner 连接(跳过 guest ws)。用于 contact:added / contact:removed /
61132
- // contact:pinned / friend:reverseTokenOffered / appBuilder:project-updated 等
60831
+ // contact:pinned / friend:reverseTokenOffered 等
61133
60832
  // owner-only push frame——guest 无需消费。noAuth localhost ws 无 ctx,视作 owner。
61134
60833
  broadcastToOwners(frame) {
61135
60834
  const gate = this.opts.authGate;
@@ -62654,15 +62353,6 @@ function createHttpRouter(deps) {
62654
62353
  if (broadcastAfterResponse?.length) deps.authedRpc.fanOutBroadcast?.(broadcastAfterResponse);
62655
62354
  return true;
62656
62355
  }
62657
- if (url.pathname.startsWith("/preview/")) {
62658
- const port = matchPreviewPort(url.pathname);
62659
- if (port != null && isPreviewPortAllowed(port, { allowedPorts: deps.previewPorts ?? [] })) {
62660
- await proxyPreviewHttp(req, res, port);
62661
- return true;
62662
- }
62663
- sendJson(res, 403, { code: "PREVIEW_PORT_FORBIDDEN", message: "preview port not allowed" });
62664
- return true;
62665
- }
62666
62356
  if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/") && !url.pathname.startsWith("/share/md/") && url.pathname !== "/attachments/upload" && url.pathname !== "/extensions/import") {
62667
62357
  return false;
62668
62358
  }
@@ -64578,7 +64268,7 @@ var ServerKeyStore = class {
64578
64268
  init_protocol();
64579
64269
 
64580
64270
  // src/handlers/index.ts
64581
- var import_node_path75 = __toESM(require("path"), 1);
64271
+ var import_node_path74 = __toESM(require("path"), 1);
64582
64272
 
64583
64273
  // src/handlers/session.ts
64584
64274
  init_protocol();
@@ -65098,29 +64788,6 @@ function buildSessionHandlers(deps) {
65098
64788
  const del = async (frame, _client, ctx) => {
65099
64789
  const args = SessionIdArgs.parse(frame);
65100
64790
  ensureSessionAccess(ctx, args.sessionId, "send");
65101
- const sessionFile = manager.findOwnedSession(args.sessionId);
65102
- const projectName = sessionFile?.appBuilderProject ?? null;
65103
- const projectStore = (sessionFile?.creatorPrincipalId ? deps.getProjectStore?.(sessionFile.creatorPrincipalId) : void 0) ?? deps.appBuilderStore;
65104
- if (projectName && deps.devServerLifecycle && projectStore) {
65105
- try {
65106
- await deps.devServerLifecycle.stopForProject(projectName);
65107
- } catch (err) {
65108
- deps.logger?.warn("session-delete.stop-dev-server-failed", {
65109
- sessionId: args.sessionId,
65110
- projectName,
65111
- error: err instanceof Error ? err.message : String(err)
65112
- });
65113
- }
65114
- try {
65115
- await projectStore.delete(projectName);
65116
- } catch (err) {
65117
- deps.logger?.warn("session-delete.delete-project-failed", {
65118
- sessionId: args.sessionId,
65119
- projectName,
65120
- error: err instanceof Error ? err.message : String(err)
65121
- });
65122
- }
65123
- }
65124
64791
  const { broadcast } = manager.delete(args);
65125
64792
  return {
65126
64793
  response: { type: "session:deleted", sessionId: args.sessionId },
@@ -66827,30 +66494,7 @@ var METHOD_GRANT_MAP = {
66827
66494
  // 设备目录 / 连接都是 owner 本机操作(用 owner 的 ttcJwt 找中心 server),guest 不可调
66828
66495
  "device:list": ADMIN_ANY,
66829
66496
  "device:connect": ADMIN_ANY,
66830
- "device:connections": ADMIN_ANY,
66831
- // ---- app-builder Project picker(spec 2026-06-01-app-builder-project-picker-design) ----
66832
- // owner-only:picker UI 给 owner 管理本机 build 中的 project(fs + 端口分配)。
66833
- // 即使 persona-app-builder 是 PreinstalledPersona,project 管理也属本机 owner 行为,
66834
- // 不暴露给 guest(远端接入者)。
66835
- "appBuilder:listProjects": ADMIN_ANY,
66836
- // getProject 走 capability-scoped(区别于其它 appBuilder 的 owner-only ADMIN_ANY):mirror/people
66837
- // 的本机 owner 在远端是 guest,需能调 getProject 拿它 mirror 的 session 的 project。dispatcher
66838
- // 放行后由 handler 内 canAccessSession(read) 按 session 可见性授权(防 guest 越权拿别人 project)。
66839
- "appBuilder:getProject": CAPABILITY_SCOPED,
66840
- "appBuilder:createProject": ADMIN_ANY,
66841
- "appBuilder:deleteProject": ADMIN_ANY,
66842
- "appBuilder:updateProjectPort": ADMIN_ANY,
66843
- // people/mirror 协同写操作(spec 2026-06-04-people-mode-app-builder-write-and-push):以下 5 个写
66844
- // method 走 CAPABILITY_SCOPED(不再 owner-only ADMIN_ANY),handler 内 ensureProjectWriteAccess
66845
- // 按 session 可见性(canAccessSession 'send')授权——mirror 的本机 owner 在远端是 guest,能 mirror
66846
- // 该 session 就能操作它绑定的 project(发布凭证是 persona 共享资产,老板决策"统一 session 授权")。
66847
- "appBuilder:setProdUrl": CAPABILITY_SCOPED,
66848
- "appBuilder:startDevServer": CAPABILITY_SCOPED,
66849
- // reportStage 仍 owner-only:assistant 走 HTTP RPC 上报(不过 WS admin grant),UI 不调它。
66850
- "appBuilder:reportStage": ADMIN_ANY,
66851
- "appBuilder:stopDevServer": CAPABILITY_SCOPED,
66852
- "appBuilder:publish": CAPABILITY_SCOPED,
66853
- "appBuilder:dismissPublishJob": CAPABILITY_SCOPED
66497
+ "device:connections": ADMIN_ANY
66854
66498
  };
66855
66499
  var HUMAN_ONLY_METHODS = [
66856
66500
  "deploy:start",
@@ -66899,7 +66543,7 @@ function computeMethodAccess(args) {
66899
66543
  }
66900
66544
 
66901
66545
  // src/version.ts
66902
- var version = "0.2.512".length > 0 ? "0.2.512" : "dev";
66546
+ var version = "0.2.514".length > 0 ? "0.2.514" : "dev";
66903
66547
 
66904
66548
  // src/cli-probe/probe.ts
66905
66549
  var fs77 = __toESM(require("fs"), 1);
@@ -67931,520 +67575,6 @@ function buildExtensionHandlers(deps) {
67931
67575
  };
67932
67576
  }
67933
67577
 
67934
- // src/app-builder/kill-port.ts
67935
- var import_node_child_process18 = require("child_process");
67936
- async function killPortOccupants(port, ownedPids, logger) {
67937
- let pids;
67938
- try {
67939
- pids = await listPidsOnPort(port);
67940
- } catch (err) {
67941
- logger?.warn("app-builder.kill-port.lsof-failed", {
67942
- port,
67943
- error: err instanceof Error ? err.message : String(err)
67944
- });
67945
- return;
67946
- }
67947
- if (pids.length === 0) {
67948
- logger?.info("app-builder.kill-port.no-occupants", { port });
67949
- return;
67950
- }
67951
- for (const pid of pids) {
67952
- if (!ownedPids.has(pid)) {
67953
- logger?.warn("app-builder.kill-port.skip-external", {
67954
- port,
67955
- pid,
67956
- hint: "pid not in supervisor owned set; might be another daemon or external process. handle manually via lsof + kill."
67957
- });
67958
- continue;
67959
- }
67960
- try {
67961
- process.kill(pid, "SIGKILL");
67962
- logger?.info("app-builder.kill-port-occupant", { port, pid });
67963
- } catch (err) {
67964
- const code = err.code;
67965
- if (code !== "ESRCH") {
67966
- logger?.warn("app-builder.kill-port-occupant.failed", {
67967
- port,
67968
- pid,
67969
- error: err instanceof Error ? err.message : String(err)
67970
- });
67971
- }
67972
- }
67973
- }
67974
- }
67975
- function listPidsOnPort(port) {
67976
- return new Promise((resolve9, reject) => {
67977
- (0, import_node_child_process18.execFile)(
67978
- "lsof",
67979
- ["-ti", `:${port}`],
67980
- { timeout: 3e3 },
67981
- (err, stdout) => {
67982
- if (err && err.code === "ENOENT") {
67983
- reject(new Error("lsof not found in PATH"));
67984
- return;
67985
- }
67986
- const text = (stdout ?? "").toString().trim();
67987
- if (!text) {
67988
- resolve9([]);
67989
- return;
67990
- }
67991
- const list = text.split("\n").map((s) => Number(s.trim())).filter((n) => Number.isInteger(n) && n > 0);
67992
- resolve9(list);
67993
- }
67994
- );
67995
- });
67996
- }
67997
-
67998
- // src/app-builder/ports.ts
67999
- var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
68000
-
68001
- // src/handlers/app-builder.ts
68002
- init_protocol();
68003
- var import_node_path72 = require("path");
68004
- var import_node_fs59 = require("fs");
68005
- var DEFAULT_TEMPLATE = "nestjs-react";
68006
- var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
68007
- function buildAppBuilderHandlers(deps) {
68008
- const { store, devServerLifecycle, sessionManager, getTunnelUrl, broadcastProjectUpdated } = deps;
68009
- function resolveStore(opts) {
68010
- if (!deps.getStore) return store;
68011
- if (opts.sessionId) {
68012
- const s = sessionManager.findOwnedSession(opts.sessionId);
68013
- if (s?.creatorPrincipalId) return deps.getStore(s.creatorPrincipalId);
68014
- }
68015
- if (opts.projectName) {
68016
- const s = sessionManager.findOwnedSessionByAppBuilderProject(opts.projectName);
68017
- if (s?.creatorPrincipalId) return deps.getStore(s.creatorPrincipalId);
68018
- }
68019
- if (opts.ctx) return deps.getStore(opts.ctx.principal.id);
68020
- return store;
68021
- }
68022
- function ensureProjectWriteAccess(ctx, opts) {
68023
- if (!ctx) return;
68024
- const sessionId = opts.sessionId ?? (opts.projectName ? sessionManager.findOwnedSessionByAppBuilderProject(opts.projectName)?.sessionId : void 0);
68025
- if (!sessionId) return;
68026
- if (!canAccessSession(ctx, sessionId, "send", {
68027
- readSession: (sid) => sessionManager.findOwnedSession(sid) ?? null
68028
- })) {
68029
- throw new ClawdError(ERROR_CODES.UNAUTHORIZED, `cannot write project of session ${sessionId}`);
68030
- }
68031
- }
68032
- const stages = /* @__PURE__ */ new Map();
68033
- function withPublishJobStatus(meta) {
68034
- if (!meta.publishJob) return void 0;
68035
- const inFlight = deps.publishService.inFlightJobId(meta.name) === meta.publishJob.jobId;
68036
- return {
68037
- ...meta.publishJob,
68038
- status: inFlight ? "in-flight" : "interrupted"
68039
- };
68040
- }
68041
- function getStageOf(name) {
68042
- const recorded = stages.get(name);
68043
- if (recorded) return recorded;
68044
- return devServerLifecycle.isRunning(name) ? { stage: "running" } : { stage: "stopped" };
68045
- }
68046
- async function setStageAndBroadcast(name, stage, reason, s = store) {
68047
- if (reason !== void 0) {
68048
- stages.set(name, { stage, reason });
68049
- } else {
68050
- stages.set(name, { stage });
68051
- }
68052
- const projects = await s.list();
68053
- const meta = projects.find((p2) => p2.name === name);
68054
- if (!meta) {
68055
- deps.logger?.warn("app-builder.stage-broadcast.project-missing", { name, stage });
68056
- return;
68057
- }
68058
- deps.logger?.info("app-builder.stage-transition", { name, stage, reason });
68059
- const publishJobWithStatus = withPublishJobStatus(meta);
68060
- broadcastProjectUpdated({
68061
- type: "appBuilder:project-updated",
68062
- project: {
68063
- ...meta,
68064
- isRunning: devServerLifecycle.isRunning(name),
68065
- boundSessionId: devServerLifecycle.boundSessionId(name),
68066
- stage,
68067
- ...reason !== void 0 ? { stageReason: reason } : {},
68068
- ...publishJobWithStatus !== void 0 ? { publishJob: publishJobWithStatus } : { publishJob: void 0 }
68069
- },
68070
- stage,
68071
- ...reason !== void 0 ? { stageReason: reason } : {}
68072
- });
68073
- }
68074
- async function listAllUsersProjects() {
68075
- if (!deps.usersRoot || !deps.getStore) return [];
68076
- const getStore = deps.getStore;
68077
- const userIds = await import_node_fs59.promises.readdir(deps.usersRoot).catch(() => []);
68078
- const perUser = await Promise.all(
68079
- userIds.map((uid) => getStore(uid).list().catch(() => []))
68080
- );
68081
- return perUser.flat();
68082
- }
68083
- function toProjectWithStatus(p2) {
68084
- const s = getStageOf(p2.name);
68085
- return {
68086
- ...p2,
68087
- isRunning: devServerLifecycle.isRunning(p2.name),
68088
- boundSessionId: devServerLifecycle.boundSessionId(p2.name),
68089
- stage: s.stage,
68090
- ...s.reason !== void 0 ? { stageReason: s.reason } : {},
68091
- publishJob: withPublishJobStatus(p2)
68092
- };
68093
- }
68094
- const listProjects = async (frame, _client, ctx) => {
68095
- const f = frame;
68096
- const sessionId = typeof f.sessionId === "string" ? f.sessionId : void 0;
68097
- let metas;
68098
- if (sessionId) {
68099
- metas = await resolveStore({ ctx, sessionId }).list();
68100
- } else if (ctx?.principal.kind === "owner" && deps.usersRoot && deps.getStore) {
68101
- metas = await listAllUsersProjects();
68102
- } else {
68103
- metas = await resolveStore({ ctx }).list();
68104
- }
68105
- return { response: { projects: metas.map(toProjectWithStatus) } };
68106
- };
68107
- const getProject = async (frame, _client, ctx) => {
68108
- const f = parseArgs2(GetProjectArgsSchema, frame, "getProject");
68109
- if (ctx && !canAccessSession(ctx, f.sessionId, "read", {
68110
- readSession: (sid) => sessionManager.findOwnedSession(sid) ?? null
68111
- })) {
68112
- throw new ClawdError(ERROR_CODES.UNAUTHORIZED, `cannot read session ${f.sessionId}`);
68113
- }
68114
- const session = sessionManager.findOwnedSession(f.sessionId);
68115
- const projectName = session?.appBuilderProject ?? null;
68116
- if (!projectName) return { response: { project: null } };
68117
- const meta = (await resolveStore({ ctx, sessionId: f.sessionId }).list()).find(
68118
- (p2) => p2.name === projectName
68119
- );
68120
- return { response: { project: meta ? toProjectWithStatus(meta) : null } };
68121
- };
68122
- const createProject = async (frame, _client, ctx) => {
68123
- const f = parseArgs2(CreateProjectArgsSchema, frame, "createProject");
68124
- const session = sessionManager.findOwnedSession(f.sessionId);
68125
- if (!session) {
68126
- throw new ClawdError(ERROR_CODES.SESSION_NOT_FOUND, `session ${f.sessionId} not found`);
68127
- }
68128
- if (!APP_BUILDER_PERSONAS.includes(session.ownerPersonaId ?? "")) {
68129
- throw new ClawdError(
68130
- ERROR_CODES.WRONG_PERSONA,
68131
- `createProject only allowed in app-builder persona sessions (got: ${session.ownerPersonaId ?? "<none>"})`
68132
- );
68133
- }
68134
- if (session.appBuilderProject) {
68135
- throw new ClawdError(
68136
- ERROR_CODES.SESSION_ALREADY_BOUND,
68137
- `session ${f.sessionId} already bound to project ${session.appBuilderProject}`
68138
- );
68139
- }
68140
- const userStore = resolveStore({ ctx, sessionId: f.sessionId });
68141
- deps.logger?.info("app-builder.create-project.start", { name: f.name, sessionId: f.sessionId });
68142
- const reservedPorts = new Set((await listAllUsersProjects()).map((p2) => p2.port));
68143
- const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
68144
- let project;
68145
- let projectDir;
68146
- try {
68147
- const created = await deps.createProjectOnDisk({
68148
- store: userStore,
68149
- name: f.name,
68150
- templateSrcDir: (0, import_node_path72.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE),
68151
- scaffoldScript: (0, import_node_path72.join)(deps.deployKitRoot, "scripts", "new-extension.sh"),
68152
- reservedPorts,
68153
- // 凭据只进 scaffold 子进程的 env,且**只给这个 persona 的 kit 模板声明要的那几个变量**
68154
- // (M1b Step 5)。整表塞进去粒度只到「进程」——那会把 service-role key 交给一个用不到
68155
- // 它的脚本。new-extension.sh 用它把模板的 `${VAR}` 渲成项目可跑的 server/.env。
68156
- extraEnv: secretsForKit(deps.readSecrets?.(), (0, import_node_path72.join)(personaRoot, "extension-kit")),
68157
- ...deps.logger ? { logger: deps.logger } : {}
68158
- });
68159
- project = created.project;
68160
- projectDir = created.projectDir;
68161
- } catch (err) {
68162
- const message = err instanceof Error ? err.message : String(err);
68163
- if (message.startsWith("scaffold failed")) {
68164
- throw new ClawdError(ERROR_CODES.INTERNAL, message);
68165
- }
68166
- throw err;
68167
- }
68168
- const { broadcast } = sessionManager.update({
68169
- sessionId: f.sessionId,
68170
- patch: { appBuilderProject: project.name }
68171
- });
68172
- await setStageAndBroadcast(project.name, "install-pending", void 0, userStore);
68173
- deps.logger?.info("app-builder.create-project.done", {
68174
- name: project.name,
68175
- port: project.port,
68176
- sessionId: f.sessionId
68177
- });
68178
- return { response: { project, projectDir }, broadcast };
68179
- };
68180
- const deleteProject = async (frame, _client, ctx) => {
68181
- const { name } = parseArgs2(DeleteProjectArgsSchema, frame, "deleteProject");
68182
- const userStore = resolveStore({ ctx, projectName: name });
68183
- await devServerLifecycle.stopForProject(name);
68184
- await userStore.delete(name);
68185
- stages.delete(name);
68186
- return { response: {} };
68187
- };
68188
- const updateProjectPort = async (frame, _client, ctx) => {
68189
- const f = parseArgs2(UpdateProjectPortArgsSchema, frame, "updateProjectPort");
68190
- const userStore = resolveStore({ ctx, projectName: f.name });
68191
- await devServerLifecycle.stopForProject(f.name);
68192
- const project = await userStore.updatePort(f.name, f.newPort);
68193
- await setStageAndBroadcast(f.name, "stopped", void 0, userStore);
68194
- return { response: { project } };
68195
- };
68196
- const setProdUrl = async (frame, _client, ctx) => {
68197
- const f = parseArgs2(SetProdUrlArgsSchema, frame, "setProdUrl");
68198
- ensureProjectWriteAccess(ctx, { projectName: f.name });
68199
- const userStore = resolveStore({ ctx, projectName: f.name });
68200
- const project = await userStore.setProdUrl(f.name, f.url);
68201
- const current = getStageOf(f.name);
68202
- await setStageAndBroadcast(f.name, current.stage, current.reason, userStore);
68203
- return { response: { project } };
68204
- };
68205
- const startDevServer = async (frame, _client, ctx) => {
68206
- const f = parseArgs2(StartDevServerArgsSchema, frame, "startDevServer");
68207
- const force = f.force === true;
68208
- ensureProjectWriteAccess(ctx, { sessionId: f.sessionId });
68209
- const session = sessionManager.findOwnedSession(f.sessionId);
68210
- if (!session) throw new ClawdError(ERROR_CODES.SESSION_NOT_FOUND, `session ${f.sessionId} not found`);
68211
- const projectName = session.appBuilderProject;
68212
- if (!projectName) {
68213
- throw new ClawdError(
68214
- ERROR_CODES.VALIDATION_ERROR,
68215
- `session ${f.sessionId} is not bound to any project; cannot start dev server`
68216
- );
68217
- }
68218
- const userStore = resolveStore({ ctx, sessionId: f.sessionId });
68219
- const projects = await userStore.list();
68220
- const meta = projects.find((p2) => p2.name === projectName);
68221
- if (!meta) {
68222
- throw new ClawdError(ERROR_CODES.SESSION_NOT_FOUND, `project ${projectName} not found on disk`);
68223
- }
68224
- const alreadyRunning = devServerLifecycle.isRunning(projectName);
68225
- deps.logger?.info("app-builder.start-dev-server.called", {
68226
- name: projectName,
68227
- sessionId: f.sessionId,
68228
- alreadyRunning,
68229
- force,
68230
- port: meta.port
68231
- });
68232
- if (alreadyRunning && !force) {
68233
- return { response: { project: meta } };
68234
- }
68235
- if (alreadyRunning && force) {
68236
- deps.logger?.info("app-builder.start-dev-server.force-stop", { name: projectName });
68237
- await devServerLifecycle.stopForProject(projectName);
68238
- }
68239
- await setStageAndBroadcast(projectName, "starting-dev-server", void 0, userStore);
68240
- const tunnelUrl = getTunnelUrl();
68241
- const tunnelHost = tunnelUrl ? new URL(tunnelUrl.replace(/^wss?:\/\//i, "https://")).host : null;
68242
- devServerLifecycle.startForSession({
68243
- sessionId: f.sessionId,
68244
- projectName,
68245
- cwd: userStore.projectDir(projectName),
68246
- port: meta.port,
68247
- tunnelHost,
68248
- devCommand: meta.devCommand
68249
- });
68250
- try {
68251
- await devServerLifecycle.awaitReady(projectName, { timeoutMs: DEV_SERVER_READY_TIMEOUT_MS });
68252
- await setStageAndBroadcast(projectName, "running", void 0, userStore);
68253
- } catch (err) {
68254
- const reason = err instanceof Error ? err.message : "dev server not ready";
68255
- deps.logger?.warn("app-builder.start-dev-server.ready-failed", {
68256
- name: projectName,
68257
- sessionId: f.sessionId,
68258
- reason
68259
- });
68260
- await setStageAndBroadcast(projectName, "failed", reason, userStore);
68261
- }
68262
- return { response: { project: meta } };
68263
- };
68264
- const reportStage = async (frame, _client, ctx) => {
68265
- const f = parseArgs2(ReportStageArgsSchema, frame, "reportStage");
68266
- if (!ASSISTANT_REPORTABLE_STAGES.includes(f.stage)) {
68267
- throw new ClawdError(
68268
- ERROR_CODES.VALIDATION_ERROR,
68269
- `reportStage: stage "${f.stage}" not reportable by assistant (allowed: ${ASSISTANT_REPORTABLE_STAGES.join(", ")})`
68270
- );
68271
- }
68272
- const session = sessionManager.findOwnedSession(f.sessionId);
68273
- if (!session) throw new ClawdError(ERROR_CODES.SESSION_NOT_FOUND, `session ${f.sessionId} not found`);
68274
- const projectName = session.appBuilderProject;
68275
- if (!projectName) {
68276
- throw new ClawdError(
68277
- ERROR_CODES.VALIDATION_ERROR,
68278
- `session ${f.sessionId} is not bound to any project`
68279
- );
68280
- }
68281
- const reason = typeof f.reason === "string" ? f.reason : void 0;
68282
- deps.logger?.info("app-builder.report-stage", {
68283
- name: projectName,
68284
- stage: f.stage,
68285
- reason,
68286
- sessionId: f.sessionId
68287
- });
68288
- const userStore = resolveStore({ ctx, sessionId: f.sessionId });
68289
- await setStageAndBroadcast(projectName, f.stage, reason, userStore);
68290
- return { response: { ok: true } };
68291
- };
68292
- const stopDevServer = async (frame, _client, ctx) => {
68293
- const f = parseArgs2(StopDevServerArgsSchema, frame, "stopDevServer");
68294
- ensureProjectWriteAccess(ctx, { sessionId: f.sessionId });
68295
- const session = sessionManager.findOwnedSession(f.sessionId);
68296
- if (!session) {
68297
- throw new ClawdError(ERROR_CODES.SESSION_NOT_FOUND, `session ${f.sessionId} not found`);
68298
- }
68299
- const projectName = session.appBuilderProject;
68300
- if (!projectName) {
68301
- throw new ClawdError(
68302
- ERROR_CODES.VALIDATION_ERROR,
68303
- `session ${f.sessionId} is not bound to any project; cannot stop dev server`
68304
- );
68305
- }
68306
- const userStore = resolveStore({ ctx, sessionId: f.sessionId });
68307
- deps.logger?.info("app-builder.stop-dev-server.called", {
68308
- name: projectName,
68309
- sessionId: f.sessionId,
68310
- isRunning: devServerLifecycle.isRunning(projectName)
68311
- });
68312
- if (devServerLifecycle.isRunning(projectName)) {
68313
- await devServerLifecycle.stopForProject(projectName);
68314
- }
68315
- try {
68316
- const projects = await userStore.list();
68317
- const meta = projects.find((p2) => p2.name === projectName);
68318
- if (meta) {
68319
- await killPortOccupants(meta.port, devServerLifecycle.getOwnedPids(), deps.logger);
68320
- }
68321
- } catch (err) {
68322
- deps.logger?.warn("app-builder.stop-dev-server.kill-port-skipped", {
68323
- name: projectName,
68324
- error: err instanceof Error ? err.message : String(err)
68325
- });
68326
- }
68327
- await setStageAndBroadcast(projectName, "stopped", void 0, userStore);
68328
- return { response: { ok: true } };
68329
- };
68330
- const publish = async (frame, _client, ctx) => {
68331
- const args = PublishArgsSchema.parse(frame);
68332
- ensureProjectWriteAccess(ctx, { projectName: args.name });
68333
- const userStore = resolveStore({ ctx, projectName: args.name });
68334
- const projects = await userStore.list();
68335
- const project = projects.find((p2) => p2.name === args.name);
68336
- if (!project) {
68337
- throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `publish: project "${args.name}" not found`);
68338
- }
68339
- const boundSession = sessionManager.findOwnedSessionByAppBuilderProject(args.name);
68340
- if (!boundSession) {
68341
- throw new ClawdError(
68342
- ERROR_CODES.VALIDATION_ERROR,
68343
- `publish: project "${args.name}" has no bound session (orphan); cannot inject failure to chat`
68344
- );
68345
- }
68346
- if (project.publishJob && deps.publishService.inFlightJobId(args.name) === null) {
68347
- deps.logger?.info("app-builder.publish.clear-interrupted", {
68348
- name: args.name,
68349
- previousJobId: project.publishJob.jobId,
68350
- previousStage: project.publishJob.stage
68351
- });
68352
- await userStore.clearPublishJob(args.name);
68353
- }
68354
- const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
68355
- const scriptPath = (0, import_node_path72.join)(deps.deployKitRoot, "scripts", "publish.sh");
68356
- deps.logger?.info("app-builder.publish.start", {
68357
- name: args.name,
68358
- sessionId: boundSession.sessionId,
68359
- scriptPath,
68360
- personaRoot
68361
- });
68362
- const result = await deps.publishService.start({
68363
- name: args.name,
68364
- sessionId: boundSession.sessionId,
68365
- scriptPath,
68366
- personaRoot,
68367
- store: userStore,
68368
- // 凭据只进 publish.sh 这个子进程的 env,且只给它的 kit 模板声明要的那几个变量
68369
- // (M1b Step 5)。那个进程里跑着 `eval "$BUILD_CMD"`——项目自带的构建命令会继承这份
68370
- // env,多给一个变量就是多交一个凭据给它。personaRoot 上面已按绑定 session 的 persona 解析。
68371
- extraEnv: secretsForKit(deps.readSecrets?.(), (0, import_node_path72.join)(personaRoot, "extension-kit")),
68372
- hooks: {
68373
- // 元数据每次写盘(publishJob 起落 / prodUrl 回填)后重推一帧:UI 渲染 PublishProgress
68374
- // 三段 checklist 依赖 project.publishJob.status==='in-flight',而 publishJob 字段只有
68375
- // 通过 project-updated 帧才能到达 UI(publish-progress 帧 UI 端只用来设 currentStage,
68376
- // 不更新 currentProject)。prodUrl 同理,靠这条链路让 PreviewPane 切线上预览。
68377
- onProjectChanged: async (name) => {
68378
- const current = getStageOf(name);
68379
- await setStageAndBroadcast(name, current.stage, current.reason, userStore);
68380
- },
68381
- broadcast: (f) => {
68382
- broadcastProjectUpdated(f);
68383
- },
68384
- injectUserText: async (sessionId, text) => {
68385
- const ownerSender = principalToSender(
68386
- makeOwnerPrincipal(
68387
- deps.ownerPrincipalId,
68388
- deps.getOwnerIdentity().displayName || "Owner",
68389
- deps.getOwnerIdentity().feishuUnionId
68390
- )
68391
- );
68392
- const { broadcast } = sessionManager.send({ sessionId, text }, ownerSender);
68393
- for (const entry of broadcast) {
68394
- deps.broadcastSessionFrame(entry.frame, entry.target);
68395
- }
68396
- }
68397
- },
68398
- ...deps.logger ? { logger: deps.logger } : {}
68399
- });
68400
- return { response: { ...result } };
68401
- };
68402
- const dismissPublishJob = async (frame, _client, ctx) => {
68403
- const args = DismissPublishJobArgsSchema.parse(frame);
68404
- ensureProjectWriteAccess(ctx, { projectName: args.name });
68405
- const userStore = resolveStore({ ctx, projectName: args.name });
68406
- const projects = await userStore.list();
68407
- const project = projects.find((p2) => p2.name === args.name);
68408
- if (!project) {
68409
- throw new ClawdError(
68410
- ERROR_CODES.VALIDATION_ERROR,
68411
- `dismissPublishJob: project "${args.name}" not found`
68412
- );
68413
- }
68414
- const inFlightJobId = deps.publishService.inFlightJobId(args.name);
68415
- if (inFlightJobId) {
68416
- deps.logger?.info("app-builder.publish.dismiss.in-flight", {
68417
- name: args.name,
68418
- jobId: inFlightJobId
68419
- });
68420
- deps.publishService.dismiss(args.name);
68421
- } else if (project.publishJob) {
68422
- deps.logger?.info("app-builder.publish.dismiss.interrupted", {
68423
- name: args.name,
68424
- jobId: project.publishJob.jobId,
68425
- stage: project.publishJob.stage
68426
- });
68427
- await userStore.clearPublishJob(args.name);
68428
- const current = getStageOf(args.name);
68429
- await setStageAndBroadcast(args.name, current.stage, current.reason, userStore);
68430
- }
68431
- return { response: { ok: true } };
68432
- };
68433
- return {
68434
- "appBuilder:listProjects": listProjects,
68435
- "appBuilder:getProject": getProject,
68436
- "appBuilder:createProject": createProject,
68437
- "appBuilder:deleteProject": deleteProject,
68438
- "appBuilder:updateProjectPort": updateProjectPort,
68439
- "appBuilder:setProdUrl": setProdUrl,
68440
- "appBuilder:startDevServer": startDevServer,
68441
- "appBuilder:reportStage": reportStage,
68442
- "appBuilder:stopDevServer": stopDevServer,
68443
- "appBuilder:publish": publish,
68444
- "appBuilder:dismissPublishJob": dismissPublishJob
68445
- };
68446
- }
68447
-
68448
67578
  // src/handlers/shift.ts
68449
67579
  function daemonShiftToUiShift(d, _nowMs) {
68450
67580
  if (d.schedule.kind === "at") {
@@ -69104,7 +68234,7 @@ function buildVisitorHandlers(deps) {
69104
68234
 
69105
68235
  // src/extension/registry.ts
69106
68236
  var import_promises16 = __toESM(require("fs/promises"), 1);
69107
- var import_node_path73 = __toESM(require("path"), 1);
68237
+ var import_node_path72 = __toESM(require("path"), 1);
69108
68238
  async function loadAll(root) {
69109
68239
  let entries;
69110
68240
  try {
@@ -69117,13 +68247,13 @@ async function loadAll(root) {
69117
68247
  for (const ent of entries) {
69118
68248
  if (!ent.isDirectory()) continue;
69119
68249
  if (ent.name.startsWith(".")) continue;
69120
- records.push(await loadOne(import_node_path73.default.join(root, ent.name), ent.name));
68250
+ records.push(await loadOne(import_node_path72.default.join(root, ent.name), ent.name));
69121
68251
  }
69122
68252
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
69123
68253
  return records;
69124
68254
  }
69125
68255
  async function loadOne(dir, dirName) {
69126
- const manifestPath = import_node_path73.default.join(dir, "manifest.json");
68256
+ const manifestPath = import_node_path72.default.join(dir, "manifest.json");
69127
68257
  let raw;
69128
68258
  try {
69129
68259
  raw = await import_promises16.default.readFile(manifestPath, "utf8");
@@ -69168,7 +68298,7 @@ async function loadOne(dir, dirName) {
69168
68298
 
69169
68299
  // src/extension/uninstall.ts
69170
68300
  var import_promises17 = __toESM(require("fs/promises"), 1);
69171
- var import_node_path74 = __toESM(require("path"), 1);
68301
+ var import_node_path73 = __toESM(require("path"), 1);
69172
68302
  var UninstallError = class extends Error {
69173
68303
  constructor(code, message) {
69174
68304
  super(message);
@@ -69177,7 +68307,7 @@ var UninstallError = class extends Error {
69177
68307
  code;
69178
68308
  };
69179
68309
  async function uninstall(deps) {
69180
- const dir = import_node_path74.default.join(deps.root, deps.extId);
68310
+ const dir = import_node_path73.default.join(deps.root, deps.extId);
69181
68311
  try {
69182
68312
  await import_promises17.default.access(dir);
69183
68313
  } catch {
@@ -69194,7 +68324,7 @@ var import_node_crypto21 = require("crypto");
69194
68324
  init_protocol();
69195
68325
 
69196
68326
  // src/peer-ops/run-command.ts
69197
- var import_node_child_process19 = require("child_process");
68327
+ var import_node_child_process18 = require("child_process");
69198
68328
  var import_node_os26 = __toESM(require("os"), 1);
69199
68329
  var DEFAULT_TIMEOUT_MS6 = 3e4;
69200
68330
  var MAX_TIMEOUT_MS = 3e5;
@@ -69202,7 +68332,7 @@ var MAX_OUTPUT_BYTES = 1048576;
69202
68332
  function runCommand2(command, opts = {}) {
69203
68333
  const timeoutMs = Math.min(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS6, MAX_TIMEOUT_MS);
69204
68334
  return new Promise((resolve9) => {
69205
- const child = (0, import_node_child_process19.spawn)("bash", ["-c", command], { cwd: import_node_os26.default.homedir() });
68335
+ const child = (0, import_node_child_process18.spawn)("bash", ["-c", command], { cwd: import_node_os26.default.homedir() });
69206
68336
  const out = [];
69207
68337
  const err = [];
69208
68338
  let outBytes = 0;
@@ -69318,9 +68448,6 @@ function buildMethodHandlers(deps) {
69318
68448
  const handlers = {
69319
68449
  ...buildSessionHandlers({
69320
68450
  ...deps,
69321
- devServerLifecycle: deps.devServerLifecycle,
69322
- appBuilderStore: deps.appBuilderStore,
69323
- getProjectStore: deps.getProjectStore,
69324
68451
  getTunnelUrl: deps.getTunnelUrl,
69325
68452
  logger: deps.logger
69326
68453
  }),
@@ -69386,28 +68513,6 @@ function buildMethodHandlers(deps) {
69386
68513
  publishedChannelStore: deps.publishedChannelStore,
69387
68514
  bundleCache: deps.bundleCache
69388
68515
  }),
69389
- ...buildAppBuilderHandlers({
69390
- store: deps.appBuilderStore,
69391
- getStore: deps.getProjectStore,
69392
- publishService: deps.publishService,
69393
- createProjectOnDisk: deps.createProjectOnDisk,
69394
- usersRoot: deps.usersRoot,
69395
- devServerLifecycle: deps.devServerLifecycle,
69396
- sessionManager: deps.manager,
69397
- ownerPrincipalId: deps.ownerPrincipalId,
69398
- getOwnerIdentity: deps.getOwnerIdentity,
69399
- getTunnelUrl: deps.getTunnelUrl,
69400
- broadcastProjectUpdated: deps.broadcastProjectUpdated,
69401
- logger: deps.logger,
69402
- // 发布上线脚手架化 (spec 2026-06-03 §5.2):
69403
- // personaRoot 仅作 resolvePersonaRoot 未注入时的兜底;deployKitRoot 为共享脚本根。
69404
- // broadcastSessionFrame 用于失败回灌路径(runner 调 manager.send 后逐帧 dispatch 到 transport)。
69405
- personaRoot: deps.appBuilderPersonaRoot,
69406
- deployKitRoot: deps.deployKitRoot,
69407
- resolvePersonaRoot: deps.resolvePersonaRoot,
69408
- readSecrets: deps.readSecrets,
69409
- broadcastSessionFrame: deps.broadcastSessionFrame
69410
- }),
69411
68516
  ...buildChannelHandlers({
69412
68517
  channelStore: deps.channelStore,
69413
68518
  topicStore: deps.topicStore,
@@ -69415,7 +68520,7 @@ function buildMethodHandlers(deps) {
69415
68520
  broadcast: deps.broadcastToOwners
69416
68521
  }),
69417
68522
  ...buildMasterHandlers({
69418
- authorityTablePath: import_node_path75.default.join(deps.masterDataDir, "master-authority.json")
68523
+ authorityTablePath: import_node_path74.default.join(deps.masterDataDir, "master-authority.json")
69419
68524
  }),
69420
68525
  ...buildTopicHandlers({
69421
68526
  topicStore: deps.topicStore,
@@ -69446,18 +68551,18 @@ function buildMethodHandlers(deps) {
69446
68551
  init_protocol();
69447
68552
 
69448
68553
  // src/tools/version-probe.ts
69449
- var import_node_child_process21 = require("child_process");
68554
+ var import_node_child_process20 = require("child_process");
69450
68555
  init_claude();
69451
68556
  init_protocol();
69452
68557
 
69453
68558
  // src/tools/shell-path.ts
69454
- var import_node_child_process20 = require("child_process");
68559
+ var import_node_child_process19 = require("child_process");
69455
68560
  var import_node_os27 = __toESM(require("os"), 1);
69456
68561
  function refreshPathFromLoginShell() {
69457
68562
  if (process.platform === "win32") return;
69458
68563
  const shell = process.env.SHELL ?? import_node_os27.default.userInfo().shell ?? "/bin/zsh";
69459
68564
  try {
69460
- const out = (0, import_node_child_process20.execFileSync)(shell, ["-ilc", 'command echo -n "$PATH"'], {
68565
+ const out = (0, import_node_child_process19.execFileSync)(shell, ["-ilc", 'command echo -n "$PATH"'], {
69461
68566
  encoding: "utf8",
69462
68567
  timeout: 3e3,
69463
68568
  stdio: ["ignore", "pipe", "ignore"]
@@ -69474,7 +68579,7 @@ function parseVersionOutput(out) {
69474
68579
  }
69475
68580
  function execVersion(bin) {
69476
68581
  return new Promise((resolve9, reject) => {
69477
- (0, import_node_child_process21.execFile)(bin, ["--version"], { timeout: 5e3 }, (err, stdout) => {
68582
+ (0, import_node_child_process20.execFile)(bin, ["--version"], { timeout: 5e3 }, (err, stdout) => {
69478
68583
  if (err) reject(err);
69479
68584
  else resolve9(String(stdout));
69480
68585
  });
@@ -69586,959 +68691,45 @@ function buildOnboardingHandlers(deps) {
69586
68691
  return { "onboarding:complete": complete, "tools:probe": toolsProbe, "onboarding:iosInterest": iosInterest };
69587
68692
  }
69588
68693
 
69589
- // src/app-builder-plugin/project-store.ts
69590
- var import_node_fs60 = require("fs");
69591
- var import_node_child_process22 = require("child_process");
69592
- var import_node_path76 = require("path");
69593
- init_protocol();
69594
- var PROJECTS_DIR = "projects";
69595
- var META_FILE = ".clawd-project.json";
69596
- var SCAFFOLD_TIMEOUT_MS = 3e4;
69597
- var metaWriteQueues = /* @__PURE__ */ new Map();
69598
- function serializeMetaWrite(metaPath, op) {
69599
- const prev = metaWriteQueues.get(metaPath) ?? Promise.resolve();
69600
- const next = prev.then(op, op);
69601
- const settled = next.then(
69602
- () => void 0,
69603
- () => void 0
69604
- );
69605
- metaWriteQueues.set(metaPath, settled);
69606
- void settled.then(() => {
69607
- if (metaWriteQueues.get(metaPath) === settled) metaWriteQueues.delete(metaPath);
69608
- });
69609
- return next;
69610
- }
69611
- var ProjectStore = class {
69612
- /** @param root projects 父目录,例如 `~/.clawd/users/<userId>/`(v2 per-user) */
69613
- constructor(root) {
69614
- this.root = root;
69615
- }
69616
- root;
69617
- serializeWrite(name, op) {
69618
- return serializeMetaWrite(this.metaPath(name), op);
69619
- }
69620
- /** projects/<name>/.clawd-project.json 路径 */
69621
- metaPath(name) {
69622
- return (0, import_node_path76.join)(this.projectsRoot(), name, META_FILE);
69623
- }
69624
- /** projects/<name>/ 目录路径(cwd 用) */
69625
- projectDir(name) {
69626
- return (0, import_node_path76.join)(this.projectsRoot(), name);
69627
- }
69628
- projectsRoot() {
69629
- return (0, import_node_path76.join)(this.root, PROJECTS_DIR);
69630
- }
69631
- async list() {
69632
- let entries;
69633
- try {
69634
- entries = await import_node_fs60.promises.readdir(this.projectsRoot());
69635
- } catch (err) {
69636
- if (err.code === "ENOENT") return [];
69637
- throw err;
69638
- }
69639
- const out = [];
69640
- for (const name of entries) {
69641
- try {
69642
- const raw = await import_node_fs60.promises.readFile(this.metaPath(name), "utf8");
69643
- const json = JSON.parse(raw);
69644
- let migrated = false;
69645
- if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
69646
- json.devCommand = DEFAULT_DEV_COMMAND;
69647
- migrated = true;
69648
- }
69649
- const parsed = ProjectMetadataSchema.safeParse(json);
69650
- if (parsed.success) {
69651
- out.push(parsed.data);
69652
- if (migrated) {
69653
- void import_node_fs60.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
69654
- });
69655
- }
69656
- }
69657
- } catch {
69658
- }
69659
- }
69660
- return out.sort((a, b2) => a.name.localeCompare(b2.name));
69661
- }
69662
- /**
69663
- * @param reservedPorts 其它 user 已占端口(v2 per-user:端口必须全局唯一,所有 user 的
69664
- * dev server 同机跑;per-user store 各自只 list 自己,靠 handler 收集全局端口传进来,
69665
- * 否则不同 user 的首个项目都从 PROJECT_PORT_MIN 起 → 全撞 6173)。
69666
- */
69667
- async create(name, reservedPorts) {
69668
- const existing = await this.list();
69669
- if (existing.some((p2) => p2.name === name)) {
69670
- throw new Error(`project "${name}" already exists / \u5DF2\u5B58\u5728`);
69671
- }
69672
- const usedPorts = new Set(existing.map((p2) => p2.port));
69673
- if (reservedPorts) for (const p2 of reservedPorts) usedPorts.add(p2);
69674
- let port = -1;
69675
- for (let p2 = PROJECT_PORT_MIN; p2 <= PROJECT_PORT_MAX; p2++) {
69676
- if (!usedPorts.has(p2)) {
69677
- port = p2;
69678
- break;
69679
- }
69680
- }
69681
- if (port < 0) {
69682
- throw new Error(
69683
- `port pool exhausted / \u7AEF\u53E3\u6C60\u5DF2\u6EE1\uFF08${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}\uFF09\uFF0C\u5148\u5220\u4E00\u4E2A project \u91CA\u653E\u7AEF\u53E3`
69684
- );
69685
- }
69686
- const meta = {
69687
- name,
69688
- port,
69689
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
69690
- devCommand: DEFAULT_DEV_COMMAND
69691
- };
69692
- const validated = ProjectMetadataSchema.safeParse(meta);
69693
- if (!validated.success) {
69694
- throw new Error(`invalid name "${name}": ${validated.error.message}`);
69695
- }
69696
- const dir = this.projectDir(name);
69697
- await import_node_fs60.promises.mkdir(dir, { recursive: true });
69698
- await import_node_fs60.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
69699
- return meta;
69700
- }
69701
- /**
69702
- * 同步内联 scaffold:spawn `extension-kit/scripts/new-extension.sh <name>` 等 exit。
69703
- * 必须在 `create()` 之后调(脚本期望 projects/<name>/.clawd-project.json 已就位 = picker 模式)。
69704
- * 失败抛错;上层(handler)应当 catch 后回滚(删 dir + 不 mutate session)。
69705
- *
69706
- * spec 2026-06-02 §5.6.3:scaffold 对 assistant 不可见,避免 assistant 被诱导去"读 example
69707
- * 代码再参考"。assistant 进 project 时目录已是完整模板,直接 pnpm install 即可。
69708
- */
69709
- async scaffold(name, templateSrcDir, scaffoldScriptPath, extraEnv) {
69710
- const destDir = this.projectDir(name);
69711
- return await new Promise((resolve9, reject) => {
69712
- const child = (0, import_node_child_process22.spawn)("bash", [scaffoldScriptPath, name, templateSrcDir, destDir], {
69713
- // extraEnv 排在 PATH **之前**:凭据表里若混进一条名叫 PATH 的条目,顶掉 PATH 会让
69714
- // 脚本以「命令找不到」的形态崩在离现场很远的地方。
69715
- env: { ...process.env, ...extraEnv ?? {}, PATH: process.env.PATH ?? "" },
69716
- stdio: ["ignore", "pipe", "pipe"]
69717
- });
69718
- let stdout = "";
69719
- let stderr = "";
69720
- child.stdout.on("data", (d) => {
69721
- stdout += d.toString("utf8");
69722
- });
69723
- child.stderr.on("data", (d) => {
69724
- stderr += d.toString("utf8");
69725
- });
69726
- const timer = setTimeout(() => {
69727
- child.kill("SIGKILL");
69728
- reject(new Error(`scaffold timeout (>${SCAFFOLD_TIMEOUT_MS}ms): ${scaffoldScriptPath} ${name}`));
69729
- }, SCAFFOLD_TIMEOUT_MS);
69730
- child.on("error", (err) => {
69731
- clearTimeout(timer);
69732
- reject(new Error(`scaffold spawn failed: ${err.message}`));
69733
- });
69734
- child.on("exit", (code, signal) => {
69735
- clearTimeout(timer);
69736
- if (code === 0) resolve9({ stdout, stderr });
69737
- else reject(new Error(`scaffold exit ${code ?? signal}: ${stderr.slice(0, 500)}`));
69738
- });
69739
- });
69740
- }
69741
- async delete(name) {
69742
- const dir = this.projectDir(name);
69743
- await import_node_fs60.promises.rm(dir, { recursive: true, force: true });
69744
- }
69745
- async updatePort(name, newPort) {
69746
- if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
69747
- throw new Error(
69748
- `port range invalid: must be ${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}, got ${newPort}`
69749
- );
69750
- }
69751
- return await this.serializeWrite(name, async () => {
69752
- const list = await this.list();
69753
- const target = list.find((p2) => p2.name === name);
69754
- if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
69755
- const conflict = list.find((p2) => p2.name !== name && p2.port === newPort);
69756
- if (conflict) {
69757
- throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
69758
- }
69759
- const updated = { ...target, port: newPort };
69760
- await import_node_fs60.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
69761
- return updated;
69762
- });
69763
- }
69764
- /**
69765
- * 单栏 refactor (spec 2026-06-02 §5.6.1):发布上线后写公网 URL 到 .clawd-project.json.prodUrl。
69766
- * UI 通过 listProjects 读到 prodUrl 后切预览栏到线上 URL,取代旧 prod marker 机制。
69767
- * 重复调用幂等(覆盖旧 URL)。url 由 ProjectMetadataSchema 校验 url() 格式。
69768
- */
69769
- async setProdUrl(name, url) {
69770
- return await this.serializeWrite(name, async () => {
69771
- const list = await this.list();
69772
- const target = list.find((p2) => p2.name === name);
69773
- if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
69774
- const updated = { ...target, prodUrl: url };
69775
- const validated = ProjectMetadataSchema.safeParse(updated);
69776
- if (!validated.success) {
69777
- throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
69778
- }
69779
- await import_node_fs60.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
69780
- return validated.data;
69781
- });
69782
- }
69783
- /**
69784
- * 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.1.4):
69785
- * 写 publishJob 字段到 .clawd-project.json。仅承载 in-flight 状态 ——
69786
- * - daemon 收到第一个 ::stage::build marker 时写入
69787
- * - 每次 stage 切换更新 stage 字段(jobId / startedAt 不变)
69788
- * - 成功 / 失败 / dismiss 时调 clearPublishJob 清掉
69789
- * - 只有 daemon 崩才会留残骸
69790
- * 写入时保留所有其他字段(prodUrl 等不受影响)。
69791
- */
69792
- async setPublishJob(name, job) {
69793
- return await this.serializeWrite(name, async () => {
69794
- const list = await this.list();
69795
- const target = list.find((p2) => p2.name === name);
69796
- if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
69797
- const updated = { ...target, publishJob: job };
69798
- const validated = ProjectMetadataSchema.safeParse(updated);
69799
- if (!validated.success) {
69800
- throw new Error(`invalid publishJob: ${validated.error.message}`);
69801
- }
69802
- await import_node_fs60.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
69803
- return validated.data;
69804
- });
69805
- }
69806
- /** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
69807
- async clearPublishJob(name) {
69808
- return await this.serializeWrite(name, async () => {
69809
- const list = await this.list();
69810
- const target = list.find((p2) => p2.name === name);
69811
- if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
69812
- if (!target.publishJob) return target;
69813
- const { publishJob: _drop, ...rest } = target;
69814
- void _drop;
69815
- const validated = ProjectMetadataSchema.safeParse(rest);
69816
- if (!validated.success) {
69817
- throw new Error(`failed to clear publishJob: ${validated.error.message}`);
69818
- }
69819
- await import_node_fs60.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
69820
- return validated.data;
69821
- });
69822
- }
69823
- };
69824
-
69825
- // src/app-builder/dev-server-supervisor.ts
69826
- var import_node_child_process23 = require("child_process");
69827
- var import_node_events2 = require("events");
69828
- var DEFAULT_READY_PATTERN = /Local:\s+https?:\/\/|Nest application successfully started|server listening on/i;
69829
- var DevServerSupervisor = class extends import_node_events2.EventEmitter {
69830
- running = /* @__PURE__ */ new Map();
69831
- // key: sessionId
69832
- logger = { warn: () => {
69833
- }, info: () => {
69834
- } };
69835
- /** daemon entry 启动后注入 logger,让 child 输出走 clawd.log */
69836
- setLogger(logger) {
69837
- this.logger = logger;
69838
- }
69839
- startForSession(args) {
69840
- if (this.running.has(args.sessionId)) return;
69841
- const augmentedPath = [
69842
- process.env.PATH ?? "",
69843
- "/opt/homebrew/bin",
69844
- "/usr/local/bin",
69845
- `${process.env.HOME}/.nvm/versions/node`,
69846
- `${process.env.HOME}/.local/share/pnpm`
69847
- ].filter(Boolean).join(":");
69848
- const env = {
69849
- ...process.env,
69850
- PATH: augmentedPath,
69851
- CLAWD_TUNNEL_HOST: args.tunnelHost ?? "",
69852
- CLAWD_PREVIEW_PORT: String(args.port)
69853
- };
69854
- const cmd = args.devCommand.replace(/\$CLAWD_PREVIEW_PORT/g, String(args.port));
69855
- this.logger.info("dev-server.start", {
69856
- projectName: args.projectName,
69857
- port: args.port,
69858
- cwd: args.cwd,
69859
- sessionId: args.sessionId,
69860
- tunnelHost: args.tunnelHost,
69861
- devCommand: cmd
69862
- });
69863
- const child = (0, import_node_child_process23.spawn)("sh", ["-c", cmd], {
69864
- cwd: args.cwd,
69865
- env,
69866
- stdio: "pipe",
69867
- detached: true
69868
- });
69869
- let readyResolve;
69870
- let readyReject;
69871
- const readyPromise = new Promise((resolve9, reject) => {
69872
- readyResolve = resolve9;
69873
- readyReject = reject;
69874
- });
69875
- readyPromise.catch(() => {
69876
- });
69877
- const readyPattern = args.readyPattern ? new RegExp(args.readyPattern) : DEFAULT_READY_PATTERN;
69878
- const entry = {
69879
- ...args,
69880
- process: child,
69881
- ready: false,
69882
- readyPromise,
69883
- readyResolve,
69884
- readyReject,
69885
- readyPattern
69886
- };
69887
- this.running.set(args.sessionId, entry);
69888
- child.stdout?.on("data", (chunk) => {
69889
- const text = chunk.toString();
69890
- this.logger.info("dev-server.stdout", {
69891
- projectName: args.projectName,
69892
- port: args.port,
69893
- chunk: text.trimEnd()
69894
- });
69895
- if (!entry.ready && entry.readyPattern.test(text)) {
69896
- entry.ready = true;
69897
- this.logger.info("dev-server.ready", {
69898
- projectName: args.projectName,
69899
- port: args.port
69900
- });
69901
- entry.readyResolve();
69902
- }
69903
- });
69904
- child.stderr?.on("data", (chunk) => {
69905
- this.logger.warn("dev-server.stderr", {
69906
- projectName: args.projectName,
69907
- port: args.port,
69908
- chunk: chunk.toString().trimEnd()
69909
- });
69910
- });
69911
- child.on("error", (err) => {
69912
- this.running.delete(args.sessionId);
69913
- this.logger.warn("dev-server.spawn-failed", {
69914
- projectName: args.projectName,
69915
- port: args.port,
69916
- error: err.message,
69917
- code: err.code
69918
- });
69919
- if (!entry.ready) {
69920
- entry.readyReject(new Error(`dev server spawn failed: ${err.message}`));
69921
- }
69922
- this.emit("devServer:failed", {
69923
- sessionId: args.sessionId,
69924
- projectName: args.projectName,
69925
- port: args.port,
69926
- exitCode: null
69927
- });
69928
- });
69929
- child.on("exit", (code) => {
69930
- this.running.delete(args.sessionId);
69931
- this.logger.info("dev-server.exit", { projectName: args.projectName, port: args.port, code });
69932
- if (!entry.ready) {
69933
- entry.readyReject(new Error(`dev server exited before ready (code=${code})`));
69934
- }
69935
- if (code !== 0 && code !== null) {
69936
- this.emit("devServer:failed", {
69937
- sessionId: args.sessionId,
69938
- projectName: args.projectName,
69939
- port: args.port,
69940
- exitCode: code
69941
- });
69942
- }
69943
- });
69944
- }
69945
- stopForSession(sessionId) {
69946
- const entry = this.running.get(sessionId);
69947
- if (!entry) return Promise.resolve();
69948
- if (!entry.ready) {
69949
- entry.readyReject(new Error("dev server stopped before ready"));
69950
- }
69951
- return new Promise((resolve9) => {
69952
- let settled = false;
69953
- let killTimer;
69954
- const cleanup = () => {
69955
- if (settled) return;
69956
- settled = true;
69957
- if (killTimer) clearTimeout(killTimer);
69958
- this.running.delete(sessionId);
69959
- resolve9();
69960
- };
69961
- entry.process.once("exit", cleanup);
69962
- const pid = entry.process.pid;
69963
- if (pid === void 0) {
69964
- cleanup();
69965
- return;
69966
- }
69967
- try {
69968
- process.kill(-pid, "SIGTERM");
69969
- } catch {
69970
- cleanup();
69971
- return;
69972
- }
69973
- killTimer = setTimeout(() => {
69974
- try {
69975
- process.kill(-pid, "SIGKILL");
69976
- } catch {
69977
- }
69978
- }, 3e3);
69979
- });
69980
- }
69981
- async stopForProject(projectName) {
69982
- const entry = [...this.running.values()].find((e) => e.projectName === projectName);
69983
- if (!entry) return;
69984
- await this.stopForSession(entry.sessionId);
69985
- }
69986
- /**
69987
- * 单栏 §5.6.5 fix: daemon shutdown 时调,停所有 active dev server。
69988
- * 不调的话 detached 子进程(F4-1 加的 detached:true)会成孤儿继续跑 + 持有端口,
69989
- * 下次 daemon 起来想 spawn 新 dev server 就 EADDRINUSE。
69990
- */
69991
- async stopAll() {
69992
- const sessionIds = [...this.running.keys()];
69993
- await Promise.all(sessionIds.map((sid) => this.stopForSession(sid)));
69994
- }
69995
- async restartForProjectAt(args) {
69996
- await this.stopForSession(args.sessionId);
69997
- this.startForSession({
69998
- sessionId: args.sessionId,
69999
- projectName: args.projectName,
70000
- port: args.newPort,
70001
- cwd: args.cwd,
70002
- tunnelHost: args.tunnelHost,
70003
- devCommand: args.devCommand
70004
- });
70005
- }
70006
- isRunning(projectName) {
70007
- return [...this.running.values()].some((e) => e.projectName === projectName);
70008
- }
70009
- /**
70010
- * 多 daemon fix (#803 follow-up): 返回 supervisor 当前 spawn 着的所有子进程 pid 集合。
70011
- * stopDevServer handler 调 killPortOccupants 时传入做白名单,避免无差别 kill 端口上的
70012
- * 进程把另一个 daemon (.clawd / .clawd-second) 的 dev server 也连带杀。
70013
- * undefined pid(spawn 还没拿到 pid 的极短窗口)会被过滤掉。
70014
- */
70015
- getOwnedPids() {
70016
- const pids = /* @__PURE__ */ new Set();
70017
- for (const entry of this.running.values()) {
70018
- const pid = entry.process.pid;
70019
- if (typeof pid === "number" && pid > 0) pids.add(pid);
70020
- }
70021
- return pids;
70022
- }
70023
- boundSessionId(projectName) {
70024
- const entry = [...this.running.values()].find((e) => e.projectName === projectName);
70025
- return entry?.sessionId ?? null;
70026
- }
70027
- /**
70028
- * 显式控制 fix §T2: 等待 dev server 就绪(stdout 命中 readyPattern)。
70029
- * spawnDevServerForSession / startDevServer 在 setStage('running') 之前 await 这个 promise,
70030
- * 避免 UI 收 running 帧时端口还没监听导致 preview-proxy 502。
70031
- *
70032
- * - resolve:stdout 命中 readyPattern(默认 "Local: http://...")
70033
- * - reject:超过 timeoutMs / signal abort / 进程 exit before ready / stopForSession 强停
70034
- * - throw(sync):project 不在 running map 里(never started 或 already stopped)
70035
- */
70036
- awaitReady(projectName, opts) {
70037
- const entry = [...this.running.values()].find((e) => e.projectName === projectName);
70038
- if (!entry) {
70039
- return Promise.reject(new Error(`dev server for project "${projectName}" not running`));
70040
- }
70041
- return new Promise((resolve9, reject) => {
70042
- let settled = false;
70043
- let timer;
70044
- const finish = (err) => {
70045
- if (settled) return;
70046
- settled = true;
70047
- if (timer) clearTimeout(timer);
70048
- if (opts.signal) opts.signal.removeEventListener("abort", onAbort);
70049
- err ? reject(err) : resolve9();
70050
- };
70051
- const onAbort = () => finish(new Error("awaitReady aborted via signal"));
70052
- entry.readyPromise.then(() => finish(), (err) => finish(err));
70053
- timer = setTimeout(
70054
- () => finish(new Error(`dev server ready timeout after ${opts.timeoutMs}ms`)),
70055
- opts.timeoutMs
70056
- );
70057
- if (opts.signal) {
70058
- if (opts.signal.aborted) {
70059
- onAbort();
70060
- return;
70061
- }
70062
- opts.signal.addEventListener("abort", onAbort, { once: true });
70063
- }
70064
- });
70065
- }
70066
- boundEntry(projectName) {
70067
- const entry = [...this.running.values()].find((e) => e.projectName === projectName);
70068
- if (!entry) return null;
70069
- return {
70070
- sessionId: entry.sessionId,
70071
- cwd: entry.cwd,
70072
- port: entry.port,
70073
- tunnelHost: entry.tunnelHost,
70074
- devCommand: entry.devCommand
70075
- };
70076
- }
70077
- };
70078
-
70079
- // src/app-builder-plugin/core.ts
70080
- var import_node_child_process25 = require("child_process");
70081
-
70082
- // src/app-builder-plugin/publish-registry.ts
70083
- var import_node_crypto22 = require("crypto");
70084
- var PublishJobRegistry = class {
70085
- jobs = /* @__PURE__ */ new Map();
70086
- has(name) {
70087
- return this.jobs.has(name);
70088
- }
70089
- get(name) {
70090
- return this.jobs.get(name);
70091
- }
70092
- /**
70093
- * args.jobId 仅用于测试注入;生产代码不传,registry 自己生成。
70094
- * args.now 仅用于测试注入;生产代码不传,用 Date.now()。
70095
- */
70096
- create(args) {
70097
- if (this.jobs.has(args.name)) {
70098
- throw new Error(`already publishing: ${args.name}`);
70099
- }
70100
- const jobId = args.jobId ?? `job-${(0, import_node_crypto22.randomUUID)()}`;
70101
- this.jobs.set(args.name, {
70102
- jobId,
70103
- name: args.name,
70104
- stage: "build",
70105
- sessionId: args.sessionId,
70106
- childProc: args.childProc,
70107
- dismissed: false,
70108
- sawAnyStage: false,
70109
- startedAt: args.now ?? Date.now()
70110
- });
70111
- return jobId;
70112
- }
70113
- updateStage(name, stage) {
70114
- const job = this.jobs.get(name);
70115
- if (!job) return;
70116
- job.stage = stage;
70117
- job.sawAnyStage = true;
70118
- }
70119
- markDismissed(name) {
70120
- const job = this.jobs.get(name);
70121
- if (!job) return;
70122
- job.dismissed = true;
70123
- }
70124
- remove(name) {
70125
- const job = this.jobs.get(name);
70126
- if (!job) return void 0;
70127
- this.jobs.delete(name);
70128
- return { sessionId: job.sessionId };
70129
- }
70130
- };
70131
-
70132
- // src/app-builder-plugin/publish-job-runner.ts
70133
- var import_node_child_process24 = require("child_process");
70134
- var import_node_fs61 = require("fs");
70135
- var import_node_path77 = require("path");
70136
-
70137
- // src/app-builder-plugin/publish-stage-parser.ts
70138
- var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
70139
- var PROD_URL_RE = /^\s*::prod-url::(\S+)\s*$/;
70140
- var URL_RE = /\bhttps?:\/\/[^\s]+/;
70141
- function parsePublishLine(line) {
70142
- const stageMatch = STAGE_RE.exec(line);
70143
- if (stageMatch) return { kind: "stage", stage: stageMatch[1] };
70144
- const urlMatch = PROD_URL_RE.exec(line);
70145
- if (urlMatch) return { kind: "prod-url", url: urlMatch[1] };
70146
- return null;
70147
- }
70148
- function extractProdUrl(text) {
70149
- const m2 = URL_RE.exec(text);
70150
- return m2 ? m2[0] : null;
70151
- }
70152
- function tailStderrLines(buf, n) {
70153
- if (!buf) return "";
70154
- const lines = buf.split("\n");
70155
- if (lines.length <= n) return buf;
70156
- return lines.slice(lines.length - n).join("\n");
70157
- }
70158
-
70159
- // src/app-builder-plugin/publish-job-runner.ts
70160
- async function startPublishJob(deps, args) {
70161
- const { registry: registry2, projectDir } = deps;
70162
- const spawn16 = deps.spawnImpl ?? import_node_child_process24.spawn;
70163
- if (registry2.has(args.name)) {
70164
- return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
70165
- }
70166
- const projDir = projectDir(args.name);
70167
- const logPath = (0, import_node_path77.join)(projDir, ".publish.log");
70168
- let logStream = null;
70169
- try {
70170
- logStream = (0, import_node_fs61.createWriteStream)(logPath, { flags: "w" });
70171
- } catch {
70172
- logStream = null;
70173
- }
70174
- const child = spawn16("bash", [args.scriptPath, projDir, args.personaRoot ?? ""], {
70175
- cwd: projDir,
70176
- // 宿主给的 env 覆盖在 process.env 之上:daemon 进程本身没有这些变量(它们只在这一个
70177
- // 子进程里活着)。云上宿主不传 extraEnv,凭据本来就在容器 env 里。
70178
- // PATH 排在最后(与 ProjectStore.scaffold 同款):extraEnv 里若混进一条名叫 PATH 的条目,
70179
- // 顶掉 PATH 会让脚本以「命令找不到」的形态崩在离现场很远的地方。
70180
- env: { ...process.env, ...args.extraEnv ?? {}, PATH: process.env.PATH ?? "" },
70181
- stdio: ["ignore", "pipe", "pipe"]
70182
- });
70183
- const jobId = registry2.create({
70184
- name: args.name,
70185
- sessionId: args.sessionId,
70186
- childProc: child
70187
- });
70188
- deps.logger?.info("app-builder.publish.spawned", {
70189
- name: args.name,
70190
- jobId,
70191
- pid: child.pid ?? null,
70192
- scriptPath: args.scriptPath,
70193
- logPath
70194
- });
70195
- child.on("error", (err) => {
70196
- deps.logger?.warn("app-builder.publish.spawn-error", {
70197
- name: args.name,
70198
- jobId,
70199
- error: err instanceof Error ? err.message : String(err)
70200
- });
70201
- void handleExit(deps, {
70202
- name: args.name,
70203
- jobId,
70204
- code: null,
70205
- signal: null,
70206
- urlCandidate: null,
70207
- stderrTail: err instanceof Error ? err.message : String(err),
70208
- logPath
70209
- });
70210
- });
70211
- let urlFromMarker = null;
70212
- let urlFallback = null;
70213
- let stdoutBuf = "";
70214
- child.stdout?.on("data", (chunk) => {
70215
- if (logStream) logStream.write(chunk);
70216
- stdoutBuf += chunk.toString();
70217
- let nlIdx;
70218
- while ((nlIdx = stdoutBuf.indexOf("\n")) >= 0) {
70219
- const line = stdoutBuf.slice(0, nlIdx);
70220
- stdoutBuf = stdoutBuf.slice(nlIdx + 1);
70221
- handleStdoutLine(deps, args.name, jobId, line);
70222
- const parsedForUrl = parsePublishLine(line);
70223
- if (parsedForUrl?.kind === "prod-url") urlFromMarker = parsedForUrl.url;
70224
- const url = extractProdUrl(line);
70225
- if (url && !urlFallback) urlFallback = url;
70226
- }
70227
- });
70228
- let stderrBuf = "";
70229
- child.stderr?.on("data", (chunk) => {
70230
- if (logStream) logStream.write(chunk);
70231
- stderrBuf += chunk.toString();
70232
- });
70233
- child.on("exit", (code, signal) => {
70234
- if (logStream) logStream.end();
70235
- const urlCandidate = urlFromMarker ?? urlFallback;
70236
- deps.logger?.info("app-builder.publish.child-exit", {
70237
- name: args.name,
70238
- jobId,
70239
- code,
70240
- signal,
70241
- hadUrl: !!urlCandidate,
70242
- urlSource: urlFromMarker ? "marker" : urlFallback ? "fallback" : "none",
70243
- stderrBytes: stderrBuf.length
70244
- });
70245
- void handleExit(deps, {
70246
- name: args.name,
70247
- jobId,
70248
- code,
70249
- signal,
70250
- urlCandidate,
70251
- stderrTail: tailStderrLines(stderrBuf, 20),
70252
- logPath
70253
- });
70254
- });
70255
- return { jobId, status: "started" };
70256
- }
70257
- function handleStdoutLine(deps, name, jobId, line) {
70258
- const parsed = parsePublishLine(line);
70259
- if (parsed?.kind !== "stage") return;
70260
- advanceStage(deps, name, jobId, parsed.stage);
70261
- }
70262
- function advanceStage(deps, name, jobId, next) {
70263
- const job = deps.registry.get(name);
70264
- if (!job) return;
70265
- const prev = job.stage;
70266
- const wasFirstStage = !job.sawAnyStage;
70267
- deps.logger?.info("app-builder.publish.stage-advance", {
70268
- name,
70269
- jobId,
70270
- prev: wasFirstStage ? null : prev,
70271
- next,
70272
- firstStage: wasFirstStage
70273
- });
70274
- if (wasFirstStage) {
70275
- deps.registry.updateStage(name, next);
70276
- void deps.setPublishJob(name, {
70277
- jobId,
70278
- stage: next,
70279
- startedAt: job.startedAt
70280
- });
70281
- deps.broadcast({
70282
- type: "appBuilder:publish-progress",
70283
- name,
70284
- jobId,
70285
- stage: next,
70286
- status: "started"
70287
- });
70288
- return;
70289
- }
70290
- if (prev === next) {
70291
- return;
70292
- }
70293
- deps.broadcast({
70294
- type: "appBuilder:publish-progress",
70295
- name,
70296
- jobId,
70297
- stage: prev,
70298
- status: "completed"
70299
- });
70300
- deps.registry.updateStage(name, next);
70301
- void deps.setPublishJob(name, {
70302
- jobId,
70303
- stage: next,
70304
- startedAt: job.startedAt
70305
- });
70306
- deps.broadcast({
70307
- type: "appBuilder:publish-progress",
70308
- name,
70309
- jobId,
70310
- stage: next,
70311
- status: "started"
70312
- });
70313
- }
70314
- async function handleExit(deps, args) {
70315
- const job = deps.registry.get(args.name);
70316
- if (args.signal === "SIGTERM" && job?.dismissed) {
70317
- try {
70318
- await deps.clearPublishJob(args.name);
70319
- } catch (err) {
70320
- console.error("[publish] clearPublishJob failed (dismissed branch)", err);
70321
- }
70322
- deps.registry.remove(args.name);
70323
- deps.onSettled?.({ kind: "dismissed" });
70324
- return;
70325
- }
70326
- if (args.code === 0 && args.urlCandidate) {
70327
- if (job?.sawAnyStage) {
70328
- deps.broadcast({
70329
- type: "appBuilder:publish-progress",
70330
- name: args.name,
70331
- jobId: args.jobId,
70332
- stage: job.stage,
70333
- status: "completed"
70334
- });
70335
- }
70336
- try {
70337
- await deps.setProdUrl(args.name, args.urlCandidate);
70338
- } catch (err) {
70339
- console.error("[publish] setProdUrl failed; falling through to failure branch", err);
70340
- await emitFailure(deps, args, "\u8C03 setProdUrl \u5931\u8D25\uFF1A" + (err instanceof Error ? err.message : String(err)));
70341
- return;
70342
- }
70343
- try {
70344
- await deps.clearPublishJob(args.name);
70345
- } catch (err) {
70346
- console.error("[publish] clearPublishJob failed (success branch)", err);
70347
- }
70348
- deps.registry.remove(args.name);
70349
- deps.onSettled?.({ kind: "published", url: args.urlCandidate });
70350
- return;
70351
- }
70352
- await emitFailure(deps, args, buildErrorSummary(args));
70353
- }
70354
- async function emitFailure(deps, args, errorSummary) {
70355
- const job = deps.registry.get(args.name);
70356
- const failStage = job?.sawAnyStage ? job.stage : "unknown";
70357
- deps.logger?.warn("app-builder.publish.failed", {
70358
- name: args.name,
70359
- jobId: args.jobId,
70360
- stage: failStage,
70361
- errorSummary: errorSummary.slice(0, 500)
70362
- });
70363
- try {
70364
- await deps.clearPublishJob(args.name);
70365
- } catch (err) {
70366
- deps.logger?.warn("app-builder.publish.clear-field-failed", {
70367
- name: args.name,
70368
- jobId: args.jobId,
70369
- error: err instanceof Error ? err.message : String(err)
70370
- });
70371
- }
70372
- const removed = deps.registry.remove(args.name);
70373
- deps.broadcast({
70374
- type: "appBuilder:publish-failed",
70375
- name: args.name,
70376
- jobId: args.jobId,
70377
- stage: failStage,
70378
- errorSummary
70379
- });
70380
- if (removed) {
70381
- const msg = formatFailureMessage(failStage, errorSummary, args.logPath);
70382
- try {
70383
- await deps.injectUserText(removed.sessionId, msg);
70384
- deps.logger?.info("app-builder.publish.failure-injected-to-chat", {
70385
- name: args.name,
70386
- jobId: args.jobId,
70387
- sessionId: removed.sessionId
70388
- });
70389
- } catch (err) {
70390
- deps.logger?.warn("app-builder.publish.inject-user-text-failed", {
70391
- name: args.name,
70392
- jobId: args.jobId,
70393
- sessionId: removed.sessionId,
70394
- error: err instanceof Error ? err.message : String(err)
70395
- });
70396
- }
70397
- } else {
70398
- deps.logger?.warn("app-builder.publish.no-registry-entry-on-failure", {
70399
- name: args.name,
70400
- jobId: args.jobId
70401
- });
70402
- }
70403
- deps.onSettled?.({ kind: "failed", stage: failStage, errorSummary });
70404
- }
70405
- function buildErrorSummary(args) {
70406
- if (args.signal) {
70407
- return `publish.sh \u88AB\u4E2D\u65AD\uFF08signal: ${args.signal}\uFF09`;
70408
- }
70409
- if (args.code === 0 && !args.urlCandidate) {
70410
- return "publish.sh \u6210\u529F\u9000\u51FA\u4F46\u672A\u8F93\u51FA\u516C\u7F51 URL";
70411
- }
70412
- return args.stderrTail || `publish.sh \u5F02\u5E38\u9000\u51FA\uFF08exit code: ${args.code}\uFF09`;
70413
- }
70414
- function formatFailureMessage(stage, errorSummary, logPath) {
70415
- return `\u53D1\u5E03\u5931\u8D25\uFF1A[${stage}] \u9636\u6BB5
70416
- ${errorSummary}
70417
-
70418
- \u5B8C\u6574\u65E5\u5FD7\uFF1A${logPath}
70419
- \u8BF7\u4F60\u63A5\u7BA1\u5B8C\u6210\u672C\u6B21\u53D1\u5E03\uFF1A\u5148\u6839\u636E\u65E5\u5FD7\u4FEE\u590D\u95EE\u9898\uFF0C\u7136\u540E\u91CD\u8DD1 publish.sh \u76F4\u5230\u4E0A\u7EBF\u6210\u529F\uFF0C\u6700\u540E\u7528 appBuilder.setProdUrl \u5199\u5165\u516C\u7F51 URL\u3002`;
70420
- }
70421
- async function recoverInterruptedJobs(deps) {
70422
- const projects = await deps.listAllProjects();
70423
- for (const p2 of projects) {
70424
- if (p2.publishJob) {
70425
- deps.log(
70426
- `[publish] found interrupted job ${p2.publishJob.jobId} for ${p2.name}, stage=${p2.publishJob.stage}`
70427
- );
70428
- }
70429
- }
70430
- }
70431
-
70432
- // src/app-builder-plugin/core.ts
70433
- function createPublishService() {
70434
- const registry2 = new PublishJobRegistry();
70435
- return {
70436
- inFlightJobId(name) {
70437
- return registry2.get(name)?.jobId ?? null;
70438
- },
70439
- async start(args) {
70440
- const { store, hooks, logger } = args;
70441
- const changed = async (name) => {
70442
- await hooks?.onProjectChanged?.(name);
70443
- };
70444
- return await startPublishJob(
70445
- {
70446
- registry: registry2,
70447
- setPublishJob: async (name, job) => {
70448
- await store.setPublishJob(name, job);
70449
- await changed(name);
70450
- },
70451
- clearPublishJob: async (name) => {
70452
- await store.clearPublishJob(name);
70453
- await changed(name);
70454
- },
70455
- setProdUrl: async (name, url) => {
70456
- await store.setProdUrl(name, url);
70457
- await changed(name);
70458
- },
70459
- broadcast: (frame) => hooks?.broadcast?.(frame),
70460
- injectUserText: async (sessionId, text) => {
70461
- await hooks?.injectUserText?.(sessionId, text);
70462
- },
70463
- projectDir: (name) => store.projectDir(name),
70464
- ...hooks?.onSettled ? { onSettled: hooks.onSettled } : {},
70465
- ...logger ? { logger } : {}
70466
- },
70467
- {
70468
- name: args.name,
70469
- sessionId: args.sessionId,
70470
- scriptPath: args.scriptPath,
70471
- personaRoot: args.personaRoot,
70472
- ...args.extraEnv ? { extraEnv: args.extraEnv } : {}
70473
- }
70474
- );
70475
- },
70476
- dismiss(name) {
70477
- const job = registry2.get(name);
70478
- if (!job) return false;
70479
- registry2.markDismissed(name);
70480
- job.childProc.kill("SIGTERM");
70481
- return true;
70482
- }
70483
- };
70484
- }
70485
- async function createProjectOnDisk(args) {
70486
- const { store, name, templateSrcDir, scaffoldScript, logger } = args;
70487
- const project = await store.create(name, args.reservedPorts);
70488
- try {
70489
- const result = await store.scaffold(name, templateSrcDir, scaffoldScript, args.extraEnv);
70490
- logger?.info("app-builder.scaffold.done", { name, stdout: result.stdout.slice(0, 200) });
70491
- } catch (err) {
70492
- const message = err instanceof Error ? err.message : String(err);
70493
- logger?.warn("app-builder.scaffold.failed", { name, error: message });
70494
- await store.delete(name).catch((delErr) => {
70495
- logger?.warn("app-builder.scaffold.rollback-failed", {
70496
- name,
70497
- error: delErr instanceof Error ? delErr.message : String(delErr)
70498
- });
70499
- });
70500
- throw new Error(`scaffold failed: ${message}`);
70501
- }
70502
- return { project, projectDir: store.projectDir(name) };
70503
- }
70504
- var REMOVE_TIMEOUT_MS = 10 * 6e4;
70505
- async function recoverInterruptedPublishJobs(store, logger) {
70506
- await recoverInterruptedJobs({
70507
- listAllProjects: async () => store.list(),
70508
- log: (msg) => logger?.info(msg)
70509
- });
70510
- }
70511
-
70512
68694
  // src/app-builder-plugin/mcp-config.ts
70513
- var import_node_fs62 = __toESM(require("fs"), 1);
70514
- var import_node_path78 = __toESM(require("path"), 1);
70515
- function appBuilderMcpConfigPath(dataDir) {
70516
- return import_node_path78.default.join(dataDir, "app-builder.mcp.json");
68695
+ var import_node_fs59 = __toESM(require("fs"), 1);
68696
+ var import_node_path75 = __toESM(require("path"), 1);
68697
+ function appBuilderMcpConfigPath(outDir, personaId2) {
68698
+ return import_node_path75.default.join(outDir, `${personaId2}.app-builder.mcp.json`);
70517
68699
  }
70518
68700
  function writeAppBuilderMcpConfig(args) {
70519
- const cfgPath = appBuilderMcpConfigPath(args.dataDir);
68701
+ const cfgPath = appBuilderMcpConfigPath(args.outDir, args.personaId);
70520
68702
  const cfg = {
70521
- _comment: "daemon \u542F\u52A8\u81EA\u52A8\u751F\u6210\uFF1Bagent spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002app-builder-plugin/mcp-server.cjs \u662F clawd-app-builder \u63D2\u4EF6\uFF08app-builder persona \u79C1\u6709\u7684\u540E\u7AEF\uFF1Aproject \u76EE\u5F55/\u5143\u6570\u636E + \u53D1\u5E03\u6D41\u6C34\u7EBF\uFF09\u3002\u6709 CLAWD_DAEMON_URL \u65F6\u8F6C\u8C03 appBuilder:* RPC\uFF08\u7AEF\u53E3\u6C60 / \u4F1A\u8BDD\u7ED1\u5B9A / UI \u5E7F\u64AD\u5F52 daemon\uFF09\uFF0C\u6CA1\u6709\u65F6\u7528\u540C\u4E00\u4EFD\u5185\u6838\u81EA\u5DF1\u5E72\uFF08\u4E91\u4E0A\u5BB9\u5668\uFF09\u3002\u975E app-builder \u7CFB persona \u7684\u4F1A\u8BDD\u91CC\u672C server \u4E0D\u6CE8\u518C\u4EFB\u4F55 tool\u3002",
68703
+ _comment: `daemon \u81EA\u52A8\u751F\u6210\uFF08${args.personaId} \u7684\u4F1A\u8BDD spawn \u524D\u91CD\u6E32\uFF09\uFF1Bagent spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002app-builder-plugin/mcp-server.cjs \u662F clawd-app-builder \u63D2\u4EF6\uFF1A\u9879\u76EE\u76EE\u5F55 / scaffold / git \u5EFA\u4ED3 / \u53D1\u5E03 / \u62C6\u9664\u90FD\u5728\u5B83\u8FDB\u7A0B\u91CC\u505A\uFF0C\u51ED\u636E\u53EA\u5728\u8FD9\u4EFD per-server env \u91CC\u3002\u975E app-builder \u7CFB persona \u7684\u4F1A\u8BDD\u4E0D\u6302\u672C server\u3002`,
70522
68704
  mcpServers: {
70523
68705
  "clawd-app-builder": {
70524
68706
  type: "stdio",
70525
68707
  command: "node",
70526
68708
  args: [args.serverScriptPath],
70527
68709
  env: {
70528
- CLAWD_DAEMON_TOKEN: args.daemonToken,
70529
- CLAWD_DAEMON_URL: args.daemonUrl,
70530
- CLAWD_APP_BUILDER_SCAFFOLD_SCRIPT: import_node_path78.default.join(args.deployKitScriptsDir, "new-extension.sh"),
70531
- CLAWD_APP_BUILDER_PUBLISH_SCRIPT: import_node_path78.default.join(args.deployKitScriptsDir, "publish.sh"),
68710
+ // 凭据排前面:固定键在后,凭据表里若混进同名条目也顶不掉路径
68711
+ ...args.secrets ?? {},
68712
+ CLAWD_APP_BUILDER_PERSONA_ROOT: args.personaRoot,
68713
+ CLAWD_APP_BUILDER_TEMPLATE_DIR: args.templateDir,
68714
+ CLAWD_APP_BUILDER_SCAFFOLD_SCRIPT: import_node_path75.default.join(args.deployKitScriptsDir, "new-extension.sh"),
68715
+ CLAWD_APP_BUILDER_PUBLISH_SCRIPT: import_node_path75.default.join(args.deployKitScriptsDir, "publish.sh"),
68716
+ CLAWD_APP_BUILDER_REMOVE_SCRIPT: import_node_path75.default.join(args.deployKitScriptsDir, "remove-extension.sh"),
70532
68717
  ...args.logPath ? { CLAWD_APP_BUILDER_LOG: args.logPath } : {}
70533
68718
  }
70534
68719
  }
70535
68720
  }
70536
68721
  };
70537
- import_node_fs62.default.mkdirSync(args.dataDir, { recursive: true });
70538
- import_node_fs62.default.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2), "utf8");
68722
+ import_node_fs59.default.mkdirSync(args.outDir, { recursive: true, mode: 448 });
68723
+ const tmp = `${cfgPath}.${process.pid}.tmp`;
68724
+ import_node_fs59.default.writeFileSync(tmp, JSON.stringify(cfg, null, 2), { encoding: "utf8", mode: 384 });
68725
+ import_node_fs59.default.renameSync(tmp, cfgPath);
70539
68726
  return cfgPath;
70540
68727
  }
70541
68728
 
68729
+ // src/app-builder-plugin/personas.ts
68730
+ var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
68731
+ var APP_BUILDER_TEMPLATE = "nestjs-react";
68732
+
70542
68733
  // src/handlers/dispatch-rpc.ts
70543
68734
  init_zod();
70544
68735
  init_protocol();
@@ -70581,12 +68772,12 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
70581
68772
  }
70582
68773
 
70583
68774
  // src/extension/runtime.ts
70584
- var import_node_child_process26 = require("child_process");
70585
- var import_node_path79 = __toESM(require("path"), 1);
68775
+ var import_node_child_process21 = require("child_process");
68776
+ var import_node_path76 = __toESM(require("path"), 1);
70586
68777
  var import_promises18 = require("timers/promises");
70587
68778
 
70588
68779
  // src/extension/port-allocator.ts
70589
- var import_node_net2 = __toESM(require("net"), 1);
68780
+ var import_node_net = __toESM(require("net"), 1);
70590
68781
  var PortExhaustedError = class extends Error {
70591
68782
  constructor(min, max) {
70592
68783
  super(`no free port in [${min},${max}]`);
@@ -70599,7 +68790,7 @@ var PortExhaustedError = class extends Error {
70599
68790
  };
70600
68791
  function probe(port) {
70601
68792
  return new Promise((resolve9) => {
70602
- const srv = import_node_net2.default.createServer();
68793
+ const srv = import_node_net.default.createServer();
70603
68794
  srv.once("error", () => resolve9(false));
70604
68795
  srv.once("listening", () => {
70605
68796
  srv.close(() => resolve9(true));
@@ -70683,13 +68874,13 @@ var Runtime = class {
70683
68874
  /\$CLAWOS_EXT_PORT/g,
70684
68875
  String(port)
70685
68876
  );
70686
- const dir = import_node_path79.default.join(this.root, extId);
68877
+ const dir = import_node_path76.default.join(this.root, extId);
70687
68878
  const env = {
70688
68879
  ...process.env,
70689
68880
  CLAWOS_EXT_PORT: String(port),
70690
68881
  CLAWOS_EXT_ID: extId
70691
68882
  };
70692
- const child = (0, import_node_child_process26.spawn)("sh", ["-c", cmd], {
68883
+ const child = (0, import_node_child_process21.spawn)("sh", ["-c", cmd], {
70693
68884
  cwd: dir,
70694
68885
  env,
70695
68886
  stdio: ["ignore", "pipe", "pipe"],
@@ -70795,7 +68986,7 @@ ${handle.stderrTail}`
70795
68986
 
70796
68987
  // src/extension/published-channels.ts
70797
68988
  var import_promises19 = __toESM(require("fs/promises"), 1);
70798
- var import_node_path80 = __toESM(require("path"), 1);
68989
+ var import_node_path77 = __toESM(require("path"), 1);
70799
68990
  init_zod();
70800
68991
  var PublishedChannelsError = class extends Error {
70801
68992
  constructor(code, message) {
@@ -70894,7 +69085,7 @@ var PublishedChannelStore = class {
70894
69085
  )
70895
69086
  };
70896
69087
  const tmp = `${this.filePath}.tmp`;
70897
- await import_promises19.default.mkdir(import_node_path80.default.dirname(this.filePath), { recursive: true });
69088
+ await import_promises19.default.mkdir(import_node_path77.default.dirname(this.filePath), { recursive: true });
70898
69089
  await import_promises19.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
70899
69090
  await import_promises19.default.rename(tmp, this.filePath);
70900
69091
  }
@@ -70902,7 +69093,7 @@ var PublishedChannelStore = class {
70902
69093
 
70903
69094
  // src/extension/bundle-cache.ts
70904
69095
  var import_promises20 = __toESM(require("fs/promises"), 1);
70905
- var import_node_path81 = __toESM(require("path"), 1);
69096
+ var import_node_path78 = __toESM(require("path"), 1);
70906
69097
  var BundleCache = class {
70907
69098
  constructor(rootDir) {
70908
69099
  this.rootDir = rootDir;
@@ -70911,14 +69102,14 @@ var BundleCache = class {
70911
69102
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
70912
69103
  async write(snapshotHash, buffer) {
70913
69104
  await import_promises20.default.mkdir(this.rootDir, { recursive: true });
70914
- const file = import_node_path81.default.join(this.rootDir, `${snapshotHash}.zip`);
69105
+ const file = import_node_path78.default.join(this.rootDir, `${snapshotHash}.zip`);
70915
69106
  const tmp = `${file}.tmp`;
70916
69107
  await import_promises20.default.writeFile(tmp, buffer, { mode: 384 });
70917
69108
  await import_promises20.default.rename(tmp, file);
70918
69109
  }
70919
69110
  /** Returns the bundle bytes, or null when the file doesn't exist. */
70920
69111
  async read(snapshotHash) {
70921
- const file = import_node_path81.default.join(this.rootDir, `${snapshotHash}.zip`);
69112
+ const file = import_node_path78.default.join(this.rootDir, `${snapshotHash}.zip`);
70922
69113
  try {
70923
69114
  return await import_promises20.default.readFile(file);
70924
69115
  } catch (e) {
@@ -70928,7 +69119,7 @@ var BundleCache = class {
70928
69119
  }
70929
69120
  /** Idempotent — missing file is not an error. */
70930
69121
  async delete(snapshotHash) {
70931
- const file = import_node_path81.default.join(this.rootDir, `${snapshotHash}.zip`);
69122
+ const file = import_node_path78.default.join(this.rootDir, `${snapshotHash}.zip`);
70932
69123
  await import_promises20.default.rm(file, { force: true });
70933
69124
  }
70934
69125
  };
@@ -70951,24 +69142,24 @@ async function startDaemon(config) {
70951
69142
  sampling: logShippingCfg.sampling,
70952
69143
  homeDir: import_node_os28.default.homedir()
70953
69144
  });
70954
- const logDir = import_node_path82.default.join(config.dataDir, "log");
70955
- import_node_fs63.default.mkdirSync(logDir, { recursive: true });
69145
+ const logDir = import_node_path79.default.join(config.dataDir, "log");
69146
+ import_node_fs60.default.mkdirSync(logDir, { recursive: true });
70956
69147
  const logger = createLogger({
70957
69148
  level: config.logLevel,
70958
- file: import_node_path82.default.join(logDir, "clawd.log"),
69149
+ file: import_node_path79.default.join(logDir, "clawd.log"),
70959
69150
  logClient
70960
69151
  });
70961
69152
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
70962
69153
  const screenIdleProbeLogger = createFileOnlyLogger({
70963
- file: import_node_path82.default.join(logDir, "screen-idle-probe.log"),
69154
+ file: import_node_path79.default.join(logDir, "screen-idle-probe.log"),
70964
69155
  level: "debug"
70965
69156
  });
70966
69157
  logger.info("screen-idle probe logger enabled", {
70967
- file: import_node_path82.default.join(logDir, "screen-idle-probe.log")
69158
+ file: import_node_path79.default.join(logDir, "screen-idle-probe.log")
70968
69159
  });
70969
69160
  const CAP_TARGETS = [
70970
- import_node_path82.default.join(logDir, "clawd.log"),
70971
- import_node_path82.default.join(logDir, "screen-idle-probe.log")
69161
+ import_node_path79.default.join(logDir, "clawd.log"),
69162
+ import_node_path79.default.join(logDir, "screen-idle-probe.log")
70972
69163
  ];
70973
69164
  const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
70974
69165
  const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
@@ -71090,8 +69281,8 @@ async function startDaemon(config) {
71090
69281
  const agents = new AgentsScanner();
71091
69282
  const history = new ClaudeHistoryReader();
71092
69283
  let transport = null;
71093
- const personaStore = new PersonaStore(import_node_path82.default.join(config.dataDir, "personas"));
71094
- const usersRoot = import_node_path82.default.join(config.dataDir, "users");
69284
+ const personaStore = new PersonaStore(import_node_path79.default.join(config.dataDir, "personas"));
69285
+ const usersRoot = import_node_path79.default.join(config.dataDir, "users");
71095
69286
  const defaultsRoot = findDefaultsRoot(logger);
71096
69287
  if (defaultsRoot) {
71097
69288
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -71115,7 +69306,7 @@ async function startDaemon(config) {
71115
69306
  retirePersonaBuilder({ store: personaStore, logger });
71116
69307
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
71117
69308
  const dispatchStore = createDispatchStore({
71118
- filePath: import_node_path82.default.join(config.dataDir, "dispatch.json"),
69309
+ filePath: import_node_path79.default.join(config.dataDir, "dispatch.json"),
71119
69310
  now: () => Date.now(),
71120
69311
  onFlushError: (err) => logger.error("dispatch store flush failed", {
71121
69312
  reason: err instanceof Error ? err.message : String(err)
@@ -71124,18 +69315,18 @@ async function startDaemon(config) {
71124
69315
  await dispatchStore.load();
71125
69316
  dispatchStore.markInterruptedAsFailed((/* @__PURE__ */ new Date()).toISOString());
71126
69317
  const channelStore = createChannelStore({
71127
- filePath: import_node_path82.default.join(config.dataDir, "channels.json"),
69318
+ filePath: import_node_path79.default.join(config.dataDir, "channels.json"),
71128
69319
  now: () => Date.now(),
71129
- genId: () => `ch-${(0, import_node_crypto23.randomUUID)().slice(0, 8)}`,
69320
+ genId: () => `ch-${(0, import_node_crypto22.randomUUID)().slice(0, 8)}`,
71130
69321
  onFlushError: (err) => logger.error("channel store flush failed", {
71131
69322
  reason: err instanceof Error ? err.message : String(err)
71132
69323
  })
71133
69324
  });
71134
69325
  await channelStore.load();
71135
69326
  const topicStore = createTopicStore({
71136
- filePath: import_node_path82.default.join(config.dataDir, "topics.json"),
69327
+ filePath: import_node_path79.default.join(config.dataDir, "topics.json"),
71137
69328
  now: () => Date.now(),
71138
- genId: () => `tp-${(0, import_node_crypto23.randomUUID)().slice(0, 8)}`,
69329
+ genId: () => `tp-${(0, import_node_crypto22.randomUUID)().slice(0, 8)}`,
71139
69330
  onFlushError: (err) => logger.error("topic store flush failed", {
71140
69331
  reason: err instanceof Error ? err.message : String(err)
71141
69332
  })
@@ -71143,7 +69334,7 @@ async function startDaemon(config) {
71143
69334
  await topicStore.load();
71144
69335
  let deliverPendingRef;
71145
69336
  const personaDispatchManager = new PersonaDispatchManager({
71146
- genId: () => (0, import_node_crypto23.randomUUID)(),
69337
+ genId: () => (0, import_node_crypto22.randomUUID)(),
71147
69338
  store: dispatchStore,
71148
69339
  now: () => Date.now(),
71149
69340
  onCompleted: (sid) => deliverPendingRef?.(sid),
@@ -71160,28 +69351,32 @@ async function startDaemon(config) {
71160
69351
  });
71161
69352
  }
71162
69353
  });
71163
- const here = typeof __dirname === "string" ? __dirname : import_node_path82.default.dirname((0, import_node_url5.fileURLToPath)(import_meta8.url));
69354
+ const here = typeof __dirname === "string" ? __dirname : import_node_path79.default.dirname((0, import_node_url5.fileURLToPath)(import_meta8.url));
71164
69355
  const mcpConfigs = [];
71165
69356
  const secretsBundleRoot = findSecretsBundleRoot(logger);
71166
- const localSecretsDir = import_node_path82.default.join(config.dataDir, LOCAL_SECRETS_DIRNAME);
69357
+ const localSecretsDir = import_node_path79.default.join(config.dataDir, LOCAL_SECRETS_DIRNAME);
71167
69358
  const readSecrets = () => readSecretTable({ bundleRoot: secretsBundleRoot, overrideDir: localSecretsDir, logger });
71168
- const resolvePersonaMcpConfig = (personaId2) => renderPersonaMcpSecrets({
71169
- personaId: personaId2,
71170
- personaDir: import_node_path82.default.join(config.dataDir, "personas", personaId2),
71171
- secrets: readSecrets(),
71172
- outDir: import_node_path82.default.join(config.dataDir, "persona-mcp"),
71173
- logger
71174
- });
69359
+ let refreshAppBuilderMcpConfig;
69360
+ const resolvePersonaMcpConfig = (personaId2) => {
69361
+ refreshAppBuilderMcpConfig?.(personaId2);
69362
+ return renderPersonaMcpSecrets({
69363
+ personaId: personaId2,
69364
+ personaDir: import_node_path79.default.join(config.dataDir, "personas", personaId2),
69365
+ secrets: readSecrets(),
69366
+ outDir: import_node_path79.default.join(config.dataDir, "persona-mcp"),
69367
+ logger
69368
+ });
69369
+ };
71175
69370
  const mcpDaemonUrl = `http://127.0.0.1:${config.port}`;
71176
69371
  const dispatchServerCandidates = [
71177
- import_node_path82.default.join(here, "dispatch", "mcp-server.cjs"),
69372
+ import_node_path79.default.join(here, "dispatch", "mcp-server.cjs"),
71178
69373
  // 生产 dist/index → dist/dispatch/mcp-server.cjs
71179
- import_node_path82.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
69374
+ import_node_path79.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
71180
69375
  // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
71181
69376
  ];
71182
- const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs63.default.existsSync(p2));
69377
+ const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs60.default.existsSync(p2));
71183
69378
  if (dispatchServerScriptPath) {
71184
- const dispatchLogPath = import_node_path82.default.join(logDir, "dispatch-mcp-server.log");
69379
+ const dispatchLogPath = import_node_path79.default.join(logDir, "dispatch-mcp-server.log");
71185
69380
  const dispatchCfgPath = writeDispatchMcpConfig({
71186
69381
  dataDir: config.dataDir,
71187
69382
  serverScriptPath: dispatchServerScriptPath,
@@ -71201,12 +69396,12 @@ async function startDaemon(config) {
71201
69396
  });
71202
69397
  }
71203
69398
  const ticketServerCandidates = [
71204
- import_node_path82.default.join(here, "ticket", "mcp-server.cjs"),
71205
- import_node_path82.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
69399
+ import_node_path79.default.join(here, "ticket", "mcp-server.cjs"),
69400
+ import_node_path79.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
71206
69401
  ];
71207
- const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs63.default.existsSync(p2));
69402
+ const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs60.default.existsSync(p2));
71208
69403
  if (ticketServerScriptPath) {
71209
- const ticketLogPath = import_node_path82.default.join(logDir, "ticket-mcp-server.log");
69404
+ const ticketLogPath = import_node_path79.default.join(logDir, "ticket-mcp-server.log");
71210
69405
  const ticketCfgPath = writeTicketMcpConfig({
71211
69406
  dataDir: config.dataDir,
71212
69407
  serverScriptPath: ticketServerScriptPath,
@@ -71228,12 +69423,12 @@ async function startDaemon(config) {
71228
69423
  });
71229
69424
  }
71230
69425
  const shiftServerCandidates = [
71231
- import_node_path82.default.join(here, "shift", "mcp-server.cjs"),
71232
- import_node_path82.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
69426
+ import_node_path79.default.join(here, "shift", "mcp-server.cjs"),
69427
+ import_node_path79.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
71233
69428
  ];
71234
- const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs63.default.existsSync(p2));
69429
+ const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs60.default.existsSync(p2));
71235
69430
  if (shiftServerScriptPath) {
71236
- const shiftLogPath = import_node_path82.default.join(logDir, "shift-mcp-server.log");
69431
+ const shiftLogPath = import_node_path79.default.join(logDir, "shift-mcp-server.log");
71237
69432
  const shiftCfgPath = await writeShiftMcpConfig({
71238
69433
  dataDir: config.dataDir,
71239
69434
  serverScriptPath: shiftServerScriptPath,
@@ -71254,12 +69449,12 @@ async function startDaemon(config) {
71254
69449
  );
71255
69450
  }
71256
69451
  const inboxServerCandidates = [
71257
- import_node_path82.default.join(here, "inbox", "mcp-server.cjs"),
71258
- import_node_path82.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
69452
+ import_node_path79.default.join(here, "inbox", "mcp-server.cjs"),
69453
+ import_node_path79.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
71259
69454
  ];
71260
- const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs63.default.existsSync(p2));
69455
+ const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs60.default.existsSync(p2));
71261
69456
  if (inboxServerScriptPath) {
71262
- const inboxLogPath = import_node_path82.default.join(logDir, "inbox-mcp-server.log");
69457
+ const inboxLogPath = import_node_path79.default.join(logDir, "inbox-mcp-server.log");
71263
69458
  const inboxCfgPath = await writeInboxMcpConfig({
71264
69459
  dataDir: config.dataDir,
71265
69460
  serverScriptPath: inboxServerScriptPath,
@@ -71280,10 +69475,10 @@ async function startDaemon(config) {
71280
69475
  );
71281
69476
  }
71282
69477
  const peerOpsServerCandidates = [
71283
- import_node_path82.default.join(here, "peer-ops", "mcp-server.cjs"),
71284
- import_node_path82.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
69478
+ import_node_path79.default.join(here, "peer-ops", "mcp-server.cjs"),
69479
+ import_node_path79.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
71285
69480
  ];
71286
- const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs63.default.existsSync(p2));
69481
+ const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs60.default.existsSync(p2));
71287
69482
  if (peerOpsServerScriptPath) {
71288
69483
  const peerOpsCfgPath = writePeerOpsMcpConfig({
71289
69484
  dataDir: config.dataDir,
@@ -71303,12 +69498,12 @@ async function startDaemon(config) {
71303
69498
  );
71304
69499
  }
71305
69500
  const rpcToolServerCandidates = [
71306
- import_node_path82.default.join(here, "rpc-tool", "mcp-server.cjs"),
71307
- import_node_path82.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
69501
+ import_node_path79.default.join(here, "rpc-tool", "mcp-server.cjs"),
69502
+ import_node_path79.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
71308
69503
  ];
71309
- const rpcToolServerScriptPath = rpcToolServerCandidates.find((p2) => import_node_fs63.default.existsSync(p2));
69504
+ const rpcToolServerScriptPath = rpcToolServerCandidates.find((p2) => import_node_fs60.default.existsSync(p2));
71310
69505
  if (rpcToolServerScriptPath) {
71311
- const rpcToolLogPath = import_node_path82.default.join(logDir, "rpc-tool-mcp-server.log");
69506
+ const rpcToolLogPath = import_node_path79.default.join(logDir, "rpc-tool-mcp-server.log");
71312
69507
  const rpcToolCfgPath = writeRpcToolMcpConfig({
71313
69508
  dataDir: config.dataDir,
71314
69509
  serverScriptPath: rpcToolServerScriptPath,
@@ -71329,26 +69524,39 @@ async function startDaemon(config) {
71329
69524
  );
71330
69525
  }
71331
69526
  const appBuilderServerCandidates = [
71332
- import_node_path82.default.join(here, "app-builder-plugin", "mcp-server.cjs"),
71333
- import_node_path82.default.join(here, "..", "dist", "app-builder-plugin", "mcp-server.cjs")
69527
+ import_node_path79.default.join(here, "app-builder-plugin", "mcp-server.cjs"),
69528
+ import_node_path79.default.join(here, "..", "dist", "app-builder-plugin", "mcp-server.cjs")
71334
69529
  ];
71335
- const appBuilderServerScriptPath = appBuilderServerCandidates.find((p2) => import_node_fs63.default.existsSync(p2));
69530
+ const appBuilderServerScriptPath = appBuilderServerCandidates.find((p2) => import_node_fs60.default.existsSync(p2));
71336
69531
  if (appBuilderServerScriptPath) {
71337
- const appBuilderLogPath = import_node_path82.default.join(logDir, "app-builder-mcp-server.log");
71338
- const appBuilderCfgPath = writeAppBuilderMcpConfig({
71339
- dataDir: config.dataDir,
71340
- serverScriptPath: appBuilderServerScriptPath,
71341
- daemonToken: resolvedAuthToken,
71342
- daemonUrl: mcpDaemonUrl,
71343
- deployKitScriptsDir: import_node_path82.default.join(config.dataDir, "deploy-kit", "scripts"),
71344
- logPath: appBuilderLogPath
71345
- });
71346
- mcpConfigs.push({ id: "app-builder", path: appBuilderCfgPath, personas: APP_BUILDER_PERSONAS });
71347
- logger.info("app-builder.mcp.json written", {
71348
- path: appBuilderCfgPath,
71349
- server: appBuilderServerScriptPath,
71350
- serverLog: appBuilderLogPath
71351
- });
69532
+ const appBuilderLogPath = import_node_path79.default.join(logDir, "app-builder-mcp-server.log");
69533
+ const writeAppBuilderConfigFor = (personaId2) => {
69534
+ const personaRoot = import_node_path79.default.join(config.dataDir, "personas", personaId2);
69535
+ const secrets = secretsForKit(readSecrets(), import_node_path79.default.join(personaRoot, "extension-kit"));
69536
+ return writeAppBuilderMcpConfig({
69537
+ outDir: import_node_path79.default.join(config.dataDir, "persona-mcp"),
69538
+ personaId: personaId2,
69539
+ serverScriptPath: appBuilderServerScriptPath,
69540
+ personaRoot,
69541
+ templateDir: import_node_path79.default.join(personaRoot, "extension-kit", "examples", APP_BUILDER_TEMPLATE),
69542
+ deployKitScriptsDir: import_node_path79.default.join(config.dataDir, "deploy-kit", "scripts"),
69543
+ ...secrets ? { secrets } : {},
69544
+ logPath: appBuilderLogPath
69545
+ });
69546
+ };
69547
+ for (const personaId2 of APP_BUILDER_PERSONAS) {
69548
+ const cfgPath = writeAppBuilderConfigFor(personaId2);
69549
+ mcpConfigs.push({ id: `app-builder:${personaId2}`, path: cfgPath, personas: [personaId2] });
69550
+ logger.info("app-builder.mcp.json written", { personaId: personaId2, path: cfgPath, server: appBuilderServerScriptPath });
69551
+ }
69552
+ refreshAppBuilderMcpConfig = (personaId2) => {
69553
+ if (!APP_BUILDER_PERSONAS.includes(personaId2)) return;
69554
+ try {
69555
+ writeAppBuilderConfigFor(personaId2);
69556
+ } catch (err) {
69557
+ logger.warn("app-builder.mcp.json refresh failed", { personaId: personaId2, error: err instanceof Error ? err.message : String(err) });
69558
+ }
69559
+ };
71352
69560
  } else {
71353
69561
  logger.warn(
71354
69562
  "app-builder-plugin/mcp-server.cjs not found; clawd-app-builder tools disabled (dev tsx \u6A21\u5F0F\u4E0B\u9700\u5148\u8DD1\u8FC7 build)",
@@ -71356,7 +69564,7 @@ async function startDaemon(config) {
71356
69564
  );
71357
69565
  }
71358
69566
  const shiftStore = createShiftStore({
71359
- filePath: import_node_path82.default.join(config.dataDir, "shift.json"),
69567
+ filePath: import_node_path79.default.join(config.dataDir, "shift.json"),
71360
69568
  ownerIdProvider: () => ownerPrincipalId,
71361
69569
  now: () => Date.now()
71362
69570
  });
@@ -71378,7 +69586,7 @@ async function startDaemon(config) {
71378
69586
  getAdapter,
71379
69587
  historyReader: history,
71380
69588
  dataDir: config.dataDir,
71381
- personaRoot: import_node_path82.default.join(config.dataDir, "personas"),
69589
+ personaRoot: import_node_path79.default.join(config.dataDir, "personas"),
71382
69590
  // dispatch / shift 自动化 spawn 按 persona 绑定 tool 起 B session(codex persona 用 codex)。
71383
69591
  // personaRegistry 声明在 manager 之后(同作用域 const,调用时机在启动完成后,无 TDZ 风险)。
71384
69592
  getPersonaDefaults: (personaId2) => personaRegistry.get(personaId2),
@@ -71435,10 +69643,10 @@ async function startDaemon(config) {
71435
69643
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
71436
69644
  attachmentGroup: {
71437
69645
  onFileEdit: (input) => {
71438
- const absPath = import_node_path82.default.isAbsolute(input.relPath) ? input.relPath : import_node_path82.default.join(input.cwd, input.relPath);
69646
+ const absPath = import_node_path79.default.isAbsolute(input.relPath) ? input.relPath : import_node_path79.default.join(input.cwd, input.relPath);
71439
69647
  let size = 0;
71440
69648
  try {
71441
- size = import_node_fs63.default.statSync(absPath).size;
69649
+ size = import_node_fs60.default.statSync(absPath).size;
71442
69650
  } catch (err) {
71443
69651
  logger.warn("attachment.onFileEdit stat failed", {
71444
69652
  sessionId: input.sessionId,
@@ -71479,9 +69687,9 @@ async function startDaemon(config) {
71479
69687
  logger.info("projects.seed-dropped", { dropped: droppedSeed.dropped, backup: droppedSeed.backup });
71480
69688
  }
71481
69689
  const projectsStore = new ProjectsStore({
71482
- file: import_node_path82.default.join(config.dataDir, "projects.json"),
69690
+ file: import_node_path79.default.join(config.dataDir, "projects.json"),
71483
69691
  clawdDir: config.dataDir,
71484
- codexStateFile: import_node_path82.default.join(import_node_os28.default.homedir(), ".codex", ".codex-global-state.json"),
69692
+ codexStateFile: import_node_path79.default.join(import_node_os28.default.homedir(), ".codex", ".codex-global-state.json"),
71485
69693
  logger
71486
69694
  });
71487
69695
  const observer = new SessionObserver({
@@ -71549,13 +69757,13 @@ async function startDaemon(config) {
71549
69757
  logger
71550
69758
  });
71551
69759
  registerAdapter("claude", claudeAdapter);
71552
- const codexIsolatedHome = import_node_path82.default.join(config.dataDir, "codex-home");
69760
+ const codexIsolatedHome = import_node_path79.default.join(config.dataDir, "codex-home");
71553
69761
  try {
71554
- import_node_fs63.default.mkdirSync(codexIsolatedHome, { recursive: true });
69762
+ import_node_fs60.default.mkdirSync(codexIsolatedHome, { recursive: true });
71555
69763
  for (const f of ["auth.json", "config.toml"]) {
71556
- const link = import_node_path82.default.join(codexIsolatedHome, f);
71557
- const target = import_node_path82.default.join(import_node_os28.default.homedir(), ".codex", f);
71558
- if (!import_node_fs63.default.existsSync(link) && import_node_fs63.default.existsSync(target)) import_node_fs63.default.symlinkSync(target, link);
69764
+ const link = import_node_path79.default.join(codexIsolatedHome, f);
69765
+ const target = import_node_path79.default.join(import_node_os28.default.homedir(), ".codex", f);
69766
+ if (!import_node_fs60.default.existsSync(link) && import_node_fs60.default.existsSync(target)) import_node_fs60.default.symlinkSync(target, link);
71559
69767
  }
71560
69768
  } catch (err) {
71561
69769
  logger.warn("codex isolated home setup failed", { err: String(err) });
@@ -71566,17 +69774,17 @@ async function startDaemon(config) {
71566
69774
  logger,
71567
69775
  historyReader: new CodexHistoryReader({
71568
69776
  roots: codexRolloutRoots(config.dataDir),
71569
- sharedHome: import_node_path82.default.join(import_node_os28.default.homedir(), ".codex"),
69777
+ sharedHome: import_node_path79.default.join(import_node_os28.default.homedir(), ".codex"),
71570
69778
  logger
71571
69779
  }),
71572
69780
  // projectPaths 每次调用现取 —— 用户新建 project 后立即生效,不用重启 daemon
71573
69781
  codexHomeFor: (cwd) => resolveCodexHome({
71574
69782
  cwd,
71575
- personaRoot: import_node_path82.default.join(config.dataDir, "personas"),
69783
+ personaRoot: import_node_path79.default.join(config.dataDir, "personas"),
71576
69784
  // 必须用 listMerged(Codex registry ∪ 叠加层):只用本地 list() 的话,用户在
71577
69785
  // Codex 里建的 project 不在表里,在它下面开的会话会被误判成隔离 → Codex 侧看不到
71578
69786
  projectPaths: projectsStore.listMerged().map((x) => x.path),
71579
- sharedHome: import_node_path82.default.join(import_node_os28.default.homedir(), ".codex"),
69787
+ sharedHome: import_node_path79.default.join(import_node_os28.default.homedir(), ".codex"),
71580
69788
  isolatedHome: codexIsolatedHome
71581
69789
  }),
71582
69790
  // 附件图片验签:与 http-router /files 路由同一把 secret
@@ -71585,10 +69793,10 @@ async function startDaemon(config) {
71585
69793
  );
71586
69794
  registerAdapter(CODEX_CLOUD_TOOL_ID, new CloudCodexAdapter({ client: cloudGateway, logger, getSignSecret: () => authFile.token }));
71587
69795
  const piMcpBridgeCandidates = [
71588
- import_node_path82.default.join(here, "pi-mcp-bridge", "extension.cjs"),
71589
- import_node_path82.default.join(here, "..", "dist", "pi-mcp-bridge", "extension.cjs")
69796
+ import_node_path79.default.join(here, "pi-mcp-bridge", "extension.cjs"),
69797
+ import_node_path79.default.join(here, "..", "dist", "pi-mcp-bridge", "extension.cjs")
71590
69798
  ];
71591
- const piMcpBridgePath = piMcpBridgeCandidates.find((p2) => import_node_fs63.default.existsSync(p2));
69799
+ const piMcpBridgePath = piMcpBridgeCandidates.find((p2) => import_node_fs60.default.existsSync(p2));
71592
69800
  if (piMcpBridgePath) {
71593
69801
  logger.info("pi-mcp-bridge extension resolved", { path: piMcpBridgePath });
71594
69802
  } else {
@@ -71782,27 +69990,12 @@ async function startDaemon(config) {
71782
69990
  now: () => Date.now(),
71783
69991
  ttlSeconds: 30 * 24 * 3600
71784
69992
  });
71785
- const getProjectStore = (userId) => new ProjectStore(deriveUserWorkDir(userId, usersRoot));
71786
- const appBuilderStore = getProjectStore(ownerPrincipalId);
71787
69993
  const attachmentUserWorkDir = (sid) => {
71788
69994
  const scope = manager.findOwnedSessionScope(sid);
71789
69995
  if (scope?.kind !== "persona") return null;
71790
69996
  const userId = scope.mode === "owner" ? ownerPrincipalId : scope.capId;
71791
69997
  return deriveUserWorkDir(userId, usersRoot);
71792
69998
  };
71793
- recoverInterruptedPublishJobs(appBuilderStore, logger).catch((err) => {
71794
- logger.warn("app-builder.publish.recover-startup-failed", {
71795
- error: err instanceof Error ? err.message : String(err)
71796
- });
71797
- });
71798
- const appBuilderPublishService = createPublishService();
71799
- const devServerSupervisor = new DevServerSupervisor();
71800
- devServerSupervisor.setLogger(logger);
71801
- const appBuilderPortRange = [];
71802
- for (let p2 = PROJECT_PORT_MIN; p2 <= PROJECT_PORT_MAX; p2++) appBuilderPortRange.push(p2);
71803
- const effectivePreviewPorts = Array.from(
71804
- /* @__PURE__ */ new Set([...config.previewPorts ?? [], ...appBuilderPortRange])
71805
- );
71806
69999
  const getOwnerIdentity = () => ({
71807
70000
  displayName: ownerDisplayName,
71808
70001
  ownerId,
@@ -71899,11 +70092,11 @@ async function startDaemon(config) {
71899
70092
  // 'persona/<pid>/owner',default 走 'default'。
71900
70093
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
71901
70094
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
71902
- personaRoot: import_node_path82.default.join(config.dataDir, "personas"),
70095
+ personaRoot: import_node_path79.default.join(config.dataDir, "personas"),
71903
70096
  usersRoot
71904
70097
  },
71905
70098
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
71906
- personaRoot: import_node_path82.default.join(config.dataDir, "personas"),
70099
+ personaRoot: import_node_path79.default.join(config.dataDir, "personas"),
71907
70100
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
71908
70101
  usersRoot,
71909
70102
  // v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
@@ -72001,53 +70194,6 @@ async function startDaemon(config) {
72001
70194
  // dispatcher 的 FEISHU_GATED_METHODS gate 已保证未登录时这些 RPC 到不了 handler,
72002
70195
  // 这里的 FEISHU_LOGIN_REQUIRED 抛错只是防御纵深。
72003
70196
  feishuDevice: feishuDeviceDeps,
72004
- // app-builder
72005
- appBuilderStore,
72006
- getProjectStore,
72007
- publishService: appBuilderPublishService,
72008
- createProjectOnDisk,
72009
- devServerLifecycle: devServerSupervisor,
72010
- // 单栏 refactor §5.6.2 stage fix:appBuilder:project-updated 帧通过 wsServer broadcast。
72011
- // 推给所有 owner 连接(picker 可能没订阅特定 session,全局 broadcast 最稳)。
72012
- // people/mirror(spec 2026-06-04-people-mode-app-builder-write-and-push):额外推给订阅该 project
72013
- // 绑定 session 的 client(mirror guest 经 session:observe 订阅),让远端协同者也看到 stage / 发布进度。
72014
- // 同一帧 owner 也走 project-updated 复用给 publish-progress/failed(app-builder.ts 内 broadcast 闭包):
72015
- // name 在 event.project.name(project-updated)或 event.name(publish 帧)。owner 双栏会同时命中
72016
- // owner-broadcast + session-broadcast,但 UI patch 幂等(同 project 覆盖)无害。
72017
- broadcastProjectUpdated: (event) => {
72018
- const ev = event;
72019
- wsServer?.broadcastToOwners(ev);
72020
- const projectName = event.project?.name ?? event.name;
72021
- const boundSid = projectName ? manager.findOwnedSessionByAppBuilderProject(projectName)?.sessionId : void 0;
72022
- if (boundSid) wsServer?.broadcastToSession(boundSid, ev);
72023
- },
72024
- // 发布上线脚手架化 (spec 2026-06-03 §5.2):
72025
- // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
72026
- // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
72027
- appBuilderPersonaRoot: import_node_path82.default.join(config.dataDir, "personas", "persona-app-builder"),
72028
- // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
72029
- deployKitRoot: import_node_path82.default.join(config.dataDir, "deploy-kit"),
72030
- // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
72031
- resolvePersonaRoot: (personaId2) => import_node_path82.default.join(config.dataDir, "personas", personaId2),
72032
- // 凭据表(M1b Step 5):与 MCP 那条同一张表、同一个来源,只是交付给另一个子进程。
72033
- readSecrets,
72034
- // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
72035
- // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
72036
- // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
72037
- broadcastSessionFrame: (frame, target) => {
72038
- if (target === "all") {
72039
- transport?.broadcastAll(frame);
72040
- return;
72041
- }
72042
- const sid = frame.sessionId;
72043
- if (!sid) return;
72044
- if (target === "first-subscriber") {
72045
- const handle = transport?.firstSubscriber(sid);
72046
- if (handle) handle.send(frame);
72047
- return;
72048
- }
72049
- transport?.broadcastToSession(sid, frame);
72050
- },
72051
70197
  logger,
72052
70198
  // shift v1:shift:list 桥接 handler 透传 ShiftStore 单例(同实例也喂给
72053
70199
  // shift-internal:* MCP loopback handler)。owner 过滤用 ownerPrincipalId(跨
@@ -72225,7 +70371,7 @@ async function startDaemon(config) {
72225
70371
  });
72226
70372
  shiftScheduler.start();
72227
70373
  logger.info("shift scheduler started", { shiftCount: shiftStore.list().length });
72228
- const larkBotStore = createLarkBotBindingStore(import_node_path82.default.join(config.dataDir, "personas"));
70374
+ const larkBotStore = createLarkBotBindingStore(import_node_path79.default.join(config.dataDir, "personas"));
72229
70375
  const larkBotCloud = createLarkBotCloudClient({
72230
70376
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
72231
70377
  apiKey: resolveClawdTicketsApiKey(),
@@ -72239,7 +70385,7 @@ async function startDaemon(config) {
72239
70385
  store: larkBotStore,
72240
70386
  fetchResource: (appId, messageId, fileKey, maxBytes, type) => larkBotCloud.fetchResource(appId, messageId, fileKey, maxBytes, type),
72241
70387
  // 图片落 guest userWorkDir 下(capId = lark-<chat_id>):CC 经 --add-dir + allowRead carve 可 Read
72242
- mediaRootFor: (chatId) => import_node_path82.default.join(deriveUserWorkDir(`lark-${chatId}`, usersRoot), "lark-media"),
70388
+ mediaRootFor: (chatId) => import_node_path79.default.join(deriveUserWorkDir(`lark-${chatId}`, usersRoot), "lark-media"),
72243
70389
  isShuttingDown: () => larkDraining,
72244
70390
  logger: {
72245
70391
  warn: (msg) => logger.warn(msg),
@@ -72392,9 +70538,7 @@ async function startDaemon(config) {
72392
70538
  }
72393
70539
  return result;
72394
70540
  }
72395
- },
72396
- // app-builder build 模式:/preview/<port>/ 反代 dev server 的端口白名单(见 spec §三.2)
72397
- previewPorts: effectivePreviewPorts
70541
+ }
72398
70542
  });
72399
70543
  wsServer = new LocalWsServer({
72400
70544
  host: config.host,
@@ -72432,8 +70576,6 @@ async function startDaemon(config) {
72432
70576
  },
72433
70577
  // file-sharing HTTP 路由复用 daemon 同端口(spec §5 第 3 条);router 自己处理 auth + 404
72434
70578
  httpRequestHandler: httpRouter,
72435
- // app-builder build 模式:/preview/<port>/ HMR websocket upgrade 转发的端口白名单(同 http-router 配置)
72436
- previewPorts: effectivePreviewPorts,
72437
70579
  // 订阅成功后给该 client 重放 in-flight pendingQuestions(plan: clawd-question-server-truth)。
72438
70580
  // daemon 是 pendingQuestions 的唯一 source of truth;新 client 接入 / 刷新页面时
72439
70581
  // 把当前所有未决 question 以 session:question 帧定向回放,让 UI 不再误显示 Ended。
@@ -72546,8 +70688,8 @@ async function startDaemon(config) {
72546
70688
  const lines = [
72547
70689
  `Tunnel: ${r.url}`,
72548
70690
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
72549
- `Frpc config: ${import_node_path82.default.join(config.dataDir, "frpc.toml")}`,
72550
- `Frpc log: ${import_node_path82.default.join(logDir, "frpc.log")}`
70691
+ `Frpc config: ${import_node_path79.default.join(config.dataDir, "frpc.toml")}`,
70692
+ `Frpc log: ${import_node_path79.default.join(logDir, "frpc.log")}`
72551
70693
  ];
72552
70694
  const width2 = Math.max(...lines.map((l) => l.length));
72553
70695
  const bar = "\u2550".repeat(width2 + 4);
@@ -72560,8 +70702,8 @@ ${bar}
72560
70702
 
72561
70703
  `);
72562
70704
  try {
72563
- const connectPath = import_node_path82.default.join(config.dataDir, "connect.txt");
72564
- import_node_fs63.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
70705
+ const connectPath = import_node_path79.default.join(config.dataDir, "connect.txt");
70706
+ import_node_fs60.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
72565
70707
  } catch {
72566
70708
  }
72567
70709
  } catch (err) {
@@ -72618,9 +70760,6 @@ ${bar}
72618
70760
  observer.stopAll();
72619
70761
  manager.stopAll();
72620
70762
  await extensionRuntime.stopAll();
72621
- await devServerSupervisor.stopAll().catch((err) => {
72622
- logger.warn("shutdown.dev-server-stopAll-failed", { error: err instanceof Error ? err.message : String(err) });
72623
- });
72624
70763
  if (tunnelMgr) {
72625
70764
  await tunnelMgr.stop();
72626
70765
  }
@@ -72648,9 +70787,9 @@ ${bar}
72648
70787
  };
72649
70788
  }
72650
70789
  function migrateDropPersonsDir(dataDir) {
72651
- const dir = import_node_path82.default.join(dataDir, "persons");
70790
+ const dir = import_node_path79.default.join(dataDir, "persons");
72652
70791
  try {
72653
- import_node_fs63.default.rmSync(dir, { recursive: true, force: true });
70792
+ import_node_fs60.default.rmSync(dir, { recursive: true, force: true });
72654
70793
  } catch {
72655
70794
  }
72656
70795
  }