@clawos-dev/clawd 0.2.108-beta.205.1e8d4c5 → 0.2.108-beta.207.1de9acd

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/cli.cjs +633 -548
  2. package/dist/persona-defaults/persona-app-builder/CLAUDE.md +145 -0
  3. package/dist/persona-defaults/persona-app-builder/extension-kit/README.md +96 -0
  4. package/dist/persona-defaults/persona-app-builder/extension-kit/config.env +20 -0
  5. package/dist/persona-defaults/persona-app-builder/extension-kit/contract/bootstrap +22 -0
  6. package/dist/persona-defaults/persona-app-builder/extension-kit/contract/s.yaml.tmpl +54 -0
  7. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/.env.example +3 -0
  8. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/.fcignore +7 -0
  9. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/nest-cli.json +8 -0
  10. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/package-lock.json +4531 -0
  11. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/package.json +26 -0
  12. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/app.module.ts +14 -0
  13. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/main.ts +14 -0
  14. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/messages/messages.controller.ts +27 -0
  15. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/messages/messages.module.ts +9 -0
  16. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/messages/messages.service.ts +38 -0
  17. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/polyfill.ts +8 -0
  18. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/tsconfig.json +14 -0
  19. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/index.html +12 -0
  20. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/package-lock.json +1680 -0
  21. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/package.json +18 -0
  22. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/src/App.jsx +161 -0
  23. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/src/main.jsx +5 -0
  24. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/vite.config.js +31 -0
  25. package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/new-extension.sh +49 -0
  26. package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/publish.sh +78 -0
  27. package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/remove-extension.sh +57 -0
  28. package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/verify.sh +20 -0
  29. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -181,12 +181,16 @@ var init_methods = __esm({
181
181
  // 无 subscription 持久化:guest 端身份完全来自文件系统 + reconciled publishedExtensions.
182
182
  "extension.install-from-channel",
183
183
  "extension.update-from-channel",
184
- // ---- auth:* 飞书统一身份 Phase 1 (spec 2026-06-01-feishu-identity-design) ----
185
- // owner-only:daemon loopback 登录流程(auth:login:start state HTTP /auth/callback
186
- // 回调落盘 auth:login:done 事件)。中央字面量见 protocol/feishu-auth.ts FEISHU_AUTH_METHODS。
187
- "auth:login:start",
188
- "auth:getIdentity",
189
- "auth:logout"
184
+ // ---- app-builder Project picker (spec 2026-06-01-app-builder-project-picker-design) ----
185
+ // persona-app-builder project 管理;schemas: ProjectMetadataSchema + 4 args/result 对。
186
+ // listProjects: readdir projects/,按目录读 .clawd-project.json
187
+ // createProject: 校验名 + 从 6173-6182 段分配端口 + 写元数据 + 创建空目录
188
+ // deleteProject: 停 dev server + rm -rf 目录 + 连带删该 project 名下所有 session
189
+ // updateProjectPort: 校验新端口段内 + 未被别的 project 占 + 重启 dev server
190
+ "appBuilder:listProjects",
191
+ "appBuilder:createProject",
192
+ "appBuilder:deleteProject",
193
+ "appBuilder:updateProjectPort"
190
194
  ];
191
195
  }
192
196
  });
@@ -248,11 +252,7 @@ var init_errors = __esm({
248
252
  INTERNAL: "INTERNAL",
249
253
  UNAUTHORIZED: "UNAUTHORIZED",
250
254
  FORBIDDEN: "FORBIDDEN",
251
- INVALID_PARAM: "INVALID_PARAM",
252
- // 飞书统一身份(spec 2026-06-01 决策 #9):People/远程体系强制飞书登录。
253
- // daemon 进程身份未激活为飞书 unionId 时,FEISHU_GATED_METHODS 内的 RPC 一律返回此错误;
254
- // UI 据此显示登录引导(区别于 UNAUTHORIZED 的越权语义)。
255
- FEISHU_LOGIN_REQUIRED: "FEISHU_LOGIN_REQUIRED"
255
+ INVALID_PARAM: "INVALID_PARAM"
256
256
  };
257
257
  ClawdError = class extends Error {
258
258
  constructor(code, message) {
@@ -682,8 +682,8 @@ var init_parseUtil = __esm({
682
682
  init_errors2();
683
683
  init_en();
684
684
  makeIssue = (params) => {
685
- const { data, path: path60, errorMaps, issueData } = params;
686
- const fullPath = [...path60, ...issueData.path || []];
685
+ const { data, path: path59, errorMaps, issueData } = params;
686
+ const fullPath = [...path59, ...issueData.path || []];
687
687
  const fullIssue = {
688
688
  ...issueData,
689
689
  path: fullPath
@@ -994,11 +994,11 @@ var init_types = __esm({
994
994
  init_parseUtil();
995
995
  init_util();
996
996
  ParseInputLazyPath = class {
997
- constructor(parent, value, path60, key) {
997
+ constructor(parent, value, path59, key) {
998
998
  this._cachedPath = [];
999
999
  this.parent = parent;
1000
1000
  this.data = value;
1001
- this._path = path60;
1001
+ this._path = path59;
1002
1002
  this._key = key;
1003
1003
  }
1004
1004
  get path() {
@@ -4532,7 +4532,7 @@ var init_persona_schemas = __esm({
4532
4532
  });
4533
4533
 
4534
4534
  // ../protocol/src/schemas.ts
4535
- var SessionStatusSchema, UsageSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, ParsedEventSchema, SessionCreateArgs, SessionIdArgs, SessionUpdateArgs, SessionSendArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsArgs, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionSubscribeArgs, SessionPinArgs, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, HistoryRecentDirsArgs, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, AuthRequestFrameSchema, AuthOkFrameSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema;
4535
+ var SessionStatusSchema, UsageSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, ParsedEventSchema, SessionCreateArgs, SessionIdArgs, SessionUpdateArgs, SessionSendArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsArgs, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionSubscribeArgs, SessionPinArgs, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, HistoryRecentDirsArgs, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, AuthRequestFrameSchema, AuthOkFrameSchema, TunnelReadyEventSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema, PROJECT_PORT_MIN, PROJECT_PORT_MAX, projectNameRegex, ProjectMetadataSchema, ProjectWithStatusSchema, ListProjectsArgsSchema, ListProjectsResultSchema, CreateProjectArgsSchema, CreateProjectResultSchema, DeleteProjectArgsSchema, DeleteProjectResultSchema, UpdateProjectPortArgsSchema, UpdateProjectPortResultSchema;
4536
4536
  var init_schemas = __esm({
4537
4537
  "../protocol/src/schemas.ts"() {
4538
4538
  "use strict";
@@ -4629,6 +4629,10 @@ var init_schemas = __esm({
4629
4629
  // owner-mode persona session 身份标识;UI 用它在 SessionList 过滤 + jump,
4630
4630
  // daemon 用它做 idempotent dedupe + spawn 时派生 ctx.personaMode='owner'
4631
4631
  ownerPersonaId: external_exports.string().min(1).optional(),
4632
+ // app-builder Project 反向索引(spec: superpowers/specs/2026-06-01-app-builder-project-picker-design.md)。
4633
+ // 当此 session 绑定到某个 build 中的 project 时填写;name 与 projects/<name>/ 子目录同名,
4634
+ // 受 projectNameSchema regex 约束。daemon session:resume 时校验该目录存在,不存在则拒。
4635
+ appBuilderProject: external_exports.string().regex(/^[a-z][a-z0-9-]{0,39}$/).optional(),
4632
4636
  // listener-mode sub-session 的原始 chatId(owner-mode 不写;listener-scope 强制写入)。
4633
4637
  // 派生 sessionId 是单向 hash `${personaId}-${tokenHash12}-${chatHash8}`,必须保留原始 chatId
4634
4638
  // 才能让 alice 重连同一 sub-session(持久化在 alice localStorage 之外,由 daemon push 同步)。
@@ -4901,7 +4905,12 @@ var init_schemas = __esm({
4901
4905
  forkedFromSessionId: external_exports.string().min(1).optional(),
4902
4906
  // owner-mode persona session:传此字段时 daemon 自行派生 cwd 和启动参数,
4903
4907
  // 且按 ownerPersonaId 做 idempotent dedupe(每 persona 永远只有一个 owner session)
4904
- ownerPersonaId: external_exports.string().min(1).optional()
4908
+ ownerPersonaId: external_exports.string().min(1).optional(),
4909
+ // app-builder Project 绑定(spec 2026-06-01-app-builder-project-picker-design)。
4910
+ // UI picker 上点 idle project / 新建 project 时传入;daemon 透传写入 SessionFile.appBuilderProject。
4911
+ // 跟 ownerPersonaId 同层但不互斥(典型场景两者都传:ownerPersonaId='persona-app-builder' +
4912
+ // appBuilderProject='<project-name>')。
4913
+ appBuilderProject: external_exports.string().regex(/^[a-z][a-z0-9-]{0,39}$/).optional()
4905
4914
  }).refine((args) => args.cwd != null || args.ownerPersonaId != null, {
4906
4915
  message: "cwd \u4E0E ownerPersonaId \u81F3\u5C11\u4F20\u4E00\u4E2A"
4907
4916
  });
@@ -4915,7 +4924,11 @@ var init_schemas = __esm({
4915
4924
  effort: external_exports.string().optional(),
4916
4925
  cwd: external_exports.string().optional(),
4917
4926
  // 用户在 Edit modal 选择的 icon 标识;UI 端做 enum 兜底
4918
- iconKey: external_exports.string().optional()
4927
+ iconKey: external_exports.string().optional(),
4928
+ // app-builder picker 在当前 session 内切 project 时写入(spec 2026-06-01-app-builder-project-picker-design)。
4929
+ // daemon session:update handler 检测此字段变化时停旧 project dev server + 起新 project dev server。
4930
+ // 设为空字符串 '' 表示解绑(picker 不暴露解绑入口,但 schema 允许)。
4931
+ appBuilderProject: external_exports.string().regex(/^[a-z][a-z0-9-]{0,39}$/).or(external_exports.literal("")).optional()
4919
4932
  })
4920
4933
  });
4921
4934
  SessionSendArgs = external_exports.object({
@@ -5112,12 +5125,6 @@ var init_schemas = __esm({
5112
5125
  * 后调 capabilityManager.recordPeerHello(capId, ownerPrincipalId, displayName)
5113
5126
  * 把 cap.peerOwnerId / firstUsedByPeerAt 字段回写,让 owner 端 UI 在 People
5114
5127
  * tab 顶层 PeerOwner 视图 + Personas tab 二级分组拿到对端身份。
5115
- *
5116
- * 飞书统一身份 Phase 1 (2026-06-01):取值升级为飞书 union_id(owner 已飞书登录时;
5117
- * 来自 whoami.owner.id → daemon ownerPrincipalId → owner-identity.json unionId)。
5118
- * 未登录 daemon 仍为 auth.json 的 owner-<uuid>。daemon 端不验证该身份真实性
5119
- * (Phase 1 信任模型,见 spec §9)。Phase 2 将以 connect token 替代(introspect
5120
- * 返回身份,删除自报字段,见 spec §10)。
5121
5128
  */
5122
5129
  selfPrincipalId: external_exports.string().min(1).optional(),
5123
5130
  /**
@@ -5132,6 +5139,11 @@ var init_schemas = __esm({
5132
5139
  version: external_exports.string().min(1).optional(),
5133
5140
  protocolVersion: external_exports.number().int().nonnegative().optional()
5134
5141
  });
5142
+ TunnelReadyEventSchema = external_exports.object({
5143
+ type: external_exports.literal("tunnel:ready"),
5144
+ url: external_exports.string().min(1),
5145
+ subdomain: external_exports.string().min(1)
5146
+ });
5135
5147
  TunnelExitedEventSchema = external_exports.object({
5136
5148
  type: external_exports.literal("tunnel:exited"),
5137
5149
  code: external_exports.number().int().nullable(),
@@ -5173,6 +5185,41 @@ var init_schemas = __esm({
5173
5185
  /** file-sharing HTTP 路由的 Authorization: Bearer token;与 WS token 解耦,HTTP 401 仅触发 ReAuth 不强踢 WS(spec §11 第 4 条) */
5174
5186
  httpToken: external_exports.string().optional()
5175
5187
  });
5188
+ PROJECT_PORT_MIN = 6173;
5189
+ PROJECT_PORT_MAX = 6182;
5190
+ projectNameRegex = /^[a-z][a-z0-9-]{0,39}$/;
5191
+ ProjectMetadataSchema = external_exports.object({
5192
+ name: external_exports.string().regex(projectNameRegex, {
5193
+ 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"
5194
+ }),
5195
+ port: external_exports.number().int().min(PROJECT_PORT_MIN).max(PROJECT_PORT_MAX),
5196
+ createdAt: external_exports.string().datetime()
5197
+ });
5198
+ ProjectWithStatusSchema = ProjectMetadataSchema.extend({
5199
+ isRunning: external_exports.boolean(),
5200
+ boundSessionId: external_exports.string().nullable()
5201
+ });
5202
+ ListProjectsArgsSchema = external_exports.object({}).strict();
5203
+ ListProjectsResultSchema = external_exports.object({
5204
+ projects: external_exports.array(ProjectWithStatusSchema)
5205
+ }).strict();
5206
+ CreateProjectArgsSchema = external_exports.object({
5207
+ name: external_exports.string()
5208
+ }).strict();
5209
+ CreateProjectResultSchema = external_exports.object({
5210
+ project: ProjectMetadataSchema
5211
+ }).strict();
5212
+ DeleteProjectArgsSchema = external_exports.object({
5213
+ name: external_exports.string()
5214
+ }).strict();
5215
+ DeleteProjectResultSchema = external_exports.object({}).strict();
5216
+ UpdateProjectPortArgsSchema = external_exports.object({
5217
+ name: external_exports.string(),
5218
+ newPort: external_exports.number().int()
5219
+ }).strict();
5220
+ UpdateProjectPortResultSchema = external_exports.object({
5221
+ project: ProjectMetadataSchema
5222
+ }).strict();
5176
5223
  }
5177
5224
  });
5178
5225
 
@@ -5581,64 +5628,6 @@ var init_extension = __esm({
5581
5628
  }
5582
5629
  });
5583
5630
 
5584
- // ../protocol/src/feishu-auth.ts
5585
- var FEISHU_GATED_METHODS, FeishuIdentitySchema, AuthLoginStartResponseSchema, AuthGetIdentityResponseSchema, AuthLogoutResponseSchema, AuthLoginDoneEventSchema, AuthLoginFailedEventSchema;
5586
- var init_feishu_auth = __esm({
5587
- "../protocol/src/feishu-auth.ts"() {
5588
- "use strict";
5589
- init_zod();
5590
- FEISHU_GATED_METHODS = [
5591
- // capability / 邀请码(生成添加码 = 把自己的身份分享出去)
5592
- "capability:list",
5593
- "capability:delete",
5594
- "personal-cap:get",
5595
- // 联系人(添加朋友 / 联系人列表 / 自动重连)
5596
- "received-capability:list",
5597
- "received-capability:add",
5598
- "received-capability:remove",
5599
- "received-capability:autoAttach",
5600
- // DM / 跨用户通知
5601
- "inbox:list",
5602
- "inbox:markRead",
5603
- "inbox:postMessage",
5604
- // mirror peer sessions(远端会话镜像)
5605
- "peerSession:upsert",
5606
- "peerSession:remove"
5607
- ];
5608
- FeishuIdentitySchema = external_exports.object({
5609
- /** 飞书 union_id(租户维度,跨应用稳定)。TTC user_info 接口 data.union_id */
5610
- unionId: external_exports.string().min(1),
5611
- /** 飞书姓名。TTC user_info 接口 data.name */
5612
- displayName: external_exports.string().min(1),
5613
- /** TTC user_info 接口 data.avatar_url,可缺省 */
5614
- avatarUrl: external_exports.string().optional()
5615
- });
5616
- AuthLoginStartResponseSchema = external_exports.object({
5617
- type: external_exports.literal("auth:login:start:ok"),
5618
- /** 完整 TTC 授权页 URL(含 callback_url + state + auto=1),UI 直接 window.open */
5619
- authUrl: external_exports.string().min(1),
5620
- /** 防 CSRF 的一次性 nonce;回调时 daemon 校验 */
5621
- state: external_exports.string().min(1)
5622
- });
5623
- AuthGetIdentityResponseSchema = external_exports.object({
5624
- type: external_exports.literal("auth:getIdentity:ok"),
5625
- /** null = 未登录 */
5626
- identity: FeishuIdentitySchema.nullable()
5627
- });
5628
- AuthLogoutResponseSchema = external_exports.object({
5629
- type: external_exports.literal("auth:logout:ok")
5630
- });
5631
- AuthLoginDoneEventSchema = external_exports.object({
5632
- type: external_exports.literal("auth:login:done"),
5633
- identity: FeishuIdentitySchema
5634
- });
5635
- AuthLoginFailedEventSchema = external_exports.object({
5636
- type: external_exports.literal("auth:login:failed"),
5637
- reason: external_exports.string()
5638
- });
5639
- }
5640
- });
5641
-
5642
5631
  // ../protocol/src/runtime.ts
5643
5632
  var init_runtime = __esm({
5644
5633
  "../protocol/src/runtime.ts"() {
@@ -5656,7 +5645,6 @@ var init_runtime = __esm({
5656
5645
  init_inbox();
5657
5646
  init_received_capability();
5658
5647
  init_extension();
5659
- init_feishu_auth();
5660
5648
  }
5661
5649
  });
5662
5650
 
@@ -5931,8 +5919,8 @@ var require_req = __commonJS({
5931
5919
  if (req.originalUrl) {
5932
5920
  _req.url = req.originalUrl;
5933
5921
  } else {
5934
- const path60 = req.path;
5935
- _req.url = typeof path60 === "string" ? path60 : req.url ? req.url.path || req.url : void 0;
5922
+ const path59 = req.path;
5923
+ _req.url = typeof path59 === "string" ? path59 : req.url ? req.url.path || req.url : void 0;
5936
5924
  }
5937
5925
  if (req.query) {
5938
5926
  _req.query = req.query;
@@ -6097,14 +6085,14 @@ var require_redact = __commonJS({
6097
6085
  }
6098
6086
  return obj;
6099
6087
  }
6100
- function parsePath(path60) {
6088
+ function parsePath(path59) {
6101
6089
  const parts = [];
6102
6090
  let current = "";
6103
6091
  let inBrackets = false;
6104
6092
  let inQuotes = false;
6105
6093
  let quoteChar = "";
6106
- for (let i = 0; i < path60.length; i++) {
6107
- const char = path60[i];
6094
+ for (let i = 0; i < path59.length; i++) {
6095
+ const char = path59[i];
6108
6096
  if (!inBrackets && char === ".") {
6109
6097
  if (current) {
6110
6098
  parts.push(current);
@@ -6235,10 +6223,10 @@ var require_redact = __commonJS({
6235
6223
  return current;
6236
6224
  }
6237
6225
  function redactPaths(obj, paths, censor, remove = false) {
6238
- for (const path60 of paths) {
6239
- const parts = parsePath(path60);
6226
+ for (const path59 of paths) {
6227
+ const parts = parsePath(path59);
6240
6228
  if (parts.includes("*")) {
6241
- redactWildcardPath(obj, parts, censor, path60, remove);
6229
+ redactWildcardPath(obj, parts, censor, path59, remove);
6242
6230
  } else {
6243
6231
  if (remove) {
6244
6232
  removeKey(obj, parts);
@@ -6323,8 +6311,8 @@ var require_redact = __commonJS({
6323
6311
  }
6324
6312
  } else {
6325
6313
  if (afterWildcard.includes("*")) {
6326
- const wrappedCensor = typeof censor === "function" ? (value, path60) => {
6327
- const fullPath = [...pathArray.slice(0, pathLength), ...path60];
6314
+ const wrappedCensor = typeof censor === "function" ? (value, path59) => {
6315
+ const fullPath = [...pathArray.slice(0, pathLength), ...path59];
6328
6316
  return censor(value, fullPath);
6329
6317
  } : censor;
6330
6318
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -6359,8 +6347,8 @@ var require_redact = __commonJS({
6359
6347
  return null;
6360
6348
  }
6361
6349
  const pathStructure = /* @__PURE__ */ new Map();
6362
- for (const path60 of pathsToClone) {
6363
- const parts = parsePath(path60);
6350
+ for (const path59 of pathsToClone) {
6351
+ const parts = parsePath(path59);
6364
6352
  let current = pathStructure;
6365
6353
  for (let i = 0; i < parts.length; i++) {
6366
6354
  const part = parts[i];
@@ -6412,24 +6400,24 @@ var require_redact = __commonJS({
6412
6400
  }
6413
6401
  return cloneSelectively(obj, pathStructure);
6414
6402
  }
6415
- function validatePath(path60) {
6416
- if (typeof path60 !== "string") {
6403
+ function validatePath(path59) {
6404
+ if (typeof path59 !== "string") {
6417
6405
  throw new Error("Paths must be (non-empty) strings");
6418
6406
  }
6419
- if (path60 === "") {
6407
+ if (path59 === "") {
6420
6408
  throw new Error("Invalid redaction path ()");
6421
6409
  }
6422
- if (path60.includes("..")) {
6423
- throw new Error(`Invalid redaction path (${path60})`);
6410
+ if (path59.includes("..")) {
6411
+ throw new Error(`Invalid redaction path (${path59})`);
6424
6412
  }
6425
- if (path60.includes(",")) {
6426
- throw new Error(`Invalid redaction path (${path60})`);
6413
+ if (path59.includes(",")) {
6414
+ throw new Error(`Invalid redaction path (${path59})`);
6427
6415
  }
6428
6416
  let bracketCount = 0;
6429
6417
  let inQuotes = false;
6430
6418
  let quoteChar = "";
6431
- for (let i = 0; i < path60.length; i++) {
6432
- const char = path60[i];
6419
+ for (let i = 0; i < path59.length; i++) {
6420
+ const char = path59[i];
6433
6421
  if ((char === '"' || char === "'") && bracketCount > 0) {
6434
6422
  if (!inQuotes) {
6435
6423
  inQuotes = true;
@@ -6443,20 +6431,20 @@ var require_redact = __commonJS({
6443
6431
  } else if (char === "]" && !inQuotes) {
6444
6432
  bracketCount--;
6445
6433
  if (bracketCount < 0) {
6446
- throw new Error(`Invalid redaction path (${path60})`);
6434
+ throw new Error(`Invalid redaction path (${path59})`);
6447
6435
  }
6448
6436
  }
6449
6437
  }
6450
6438
  if (bracketCount !== 0) {
6451
- throw new Error(`Invalid redaction path (${path60})`);
6439
+ throw new Error(`Invalid redaction path (${path59})`);
6452
6440
  }
6453
6441
  }
6454
6442
  function validatePaths(paths) {
6455
6443
  if (!Array.isArray(paths)) {
6456
6444
  throw new TypeError("paths must be an array");
6457
6445
  }
6458
- for (const path60 of paths) {
6459
- validatePath(path60);
6446
+ for (const path59 of paths) {
6447
+ validatePath(path59);
6460
6448
  }
6461
6449
  }
6462
6450
  function slowRedact(options = {}) {
@@ -6624,8 +6612,8 @@ var require_redaction = __commonJS({
6624
6612
  if (shape[k2] === null) {
6625
6613
  o[k2] = (value) => topCensor(value, [k2]);
6626
6614
  } else {
6627
- const wrappedCensor = typeof censor === "function" ? (value, path60) => {
6628
- return censor(value, [k2, ...path60]);
6615
+ const wrappedCensor = typeof censor === "function" ? (value, path59) => {
6616
+ return censor(value, [k2, ...path59]);
6629
6617
  } : censor;
6630
6618
  o[k2] = Redact({
6631
6619
  paths: shape[k2],
@@ -6844,9 +6832,9 @@ var require_sonic_boom = __commonJS({
6844
6832
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
6845
6833
  "use strict";
6846
6834
  var fs48 = require("fs");
6847
- var EventEmitter2 = require("events");
6835
+ var EventEmitter3 = require("events");
6848
6836
  var inherits = require("util").inherits;
6849
- var path60 = require("path");
6837
+ var path59 = require("path");
6850
6838
  var sleep = require_atomic_sleep();
6851
6839
  var assert = require("assert");
6852
6840
  var BUSY_WRITE_TIMEOUT = 100;
@@ -6900,7 +6888,7 @@ var require_sonic_boom = __commonJS({
6900
6888
  const mode = sonic.mode;
6901
6889
  if (sonic.sync) {
6902
6890
  try {
6903
- if (sonic.mkdir) fs48.mkdirSync(path60.dirname(file), { recursive: true });
6891
+ if (sonic.mkdir) fs48.mkdirSync(path59.dirname(file), { recursive: true });
6904
6892
  const fd = fs48.openSync(file, flags, mode);
6905
6893
  fileOpened(null, fd);
6906
6894
  } catch (err) {
@@ -6908,7 +6896,7 @@ var require_sonic_boom = __commonJS({
6908
6896
  throw err;
6909
6897
  }
6910
6898
  } else if (sonic.mkdir) {
6911
- fs48.mkdir(path60.dirname(file), { recursive: true }, (err) => {
6899
+ fs48.mkdir(path59.dirname(file), { recursive: true }, (err) => {
6912
6900
  if (err) return fileOpened(err);
6913
6901
  fs48.open(file, flags, mode, fileOpened);
6914
6902
  });
@@ -7081,7 +7069,7 @@ var require_sonic_boom = __commonJS({
7081
7069
  sonic._asyncDrainScheduled = false;
7082
7070
  sonic.emit("drain");
7083
7071
  }
7084
- inherits(SonicBoom, EventEmitter2);
7072
+ inherits(SonicBoom, EventEmitter3);
7085
7073
  function mergeBuf(bufs, len) {
7086
7074
  if (bufs.length === 0) {
7087
7075
  return kEmptyBuffer;
@@ -7659,9 +7647,9 @@ var require_thread_stream = __commonJS({
7659
7647
  "../node_modules/.pnpm/thread-stream@3.1.0/node_modules/thread-stream/index.js"(exports2, module2) {
7660
7648
  "use strict";
7661
7649
  var { version: version2 } = require_package();
7662
- var { EventEmitter: EventEmitter2 } = require("events");
7650
+ var { EventEmitter: EventEmitter3 } = require("events");
7663
7651
  var { Worker } = require("worker_threads");
7664
- var { join: join11 } = require("path");
7652
+ var { join: join12 } = require("path");
7665
7653
  var { pathToFileURL } = require("url");
7666
7654
  var { wait } = require_wait();
7667
7655
  var {
@@ -7697,7 +7685,7 @@ var require_thread_stream = __commonJS({
7697
7685
  function createWorker(stream, opts) {
7698
7686
  const { filename, workerData } = opts;
7699
7687
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
7700
- const toExecute = bundlerOverrides["thread-stream-worker"] || join11(__dirname, "lib", "worker.js");
7688
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join12(__dirname, "lib", "worker.js");
7701
7689
  const worker = new Worker(toExecute, {
7702
7690
  ...opts.workerOpts,
7703
7691
  trackUnmanagedFds: false,
@@ -7815,7 +7803,7 @@ var require_thread_stream = __commonJS({
7815
7803
  stream.worker.off("exit", onWorkerExit);
7816
7804
  destroy(stream, code !== 0 ? new Error("the worker thread exited") : null);
7817
7805
  }
7818
- var ThreadStream = class extends EventEmitter2 {
7806
+ var ThreadStream = class extends EventEmitter3 {
7819
7807
  constructor(opts = {}) {
7820
7808
  super();
7821
7809
  if (opts.bufferSize < 4) {
@@ -8083,7 +8071,7 @@ var require_transport = __commonJS({
8083
8071
  "use strict";
8084
8072
  var { createRequire } = require("module");
8085
8073
  var getCallers = require_caller();
8086
- var { join: join11, isAbsolute: isAbsolute2, sep: sep3 } = require("path");
8074
+ var { join: join12, isAbsolute: isAbsolute2, sep: sep3 } = require("path");
8087
8075
  var sleep = require_atomic_sleep();
8088
8076
  var onExit = require_on_exit_leak_free();
8089
8077
  var ThreadStream = require_thread_stream();
@@ -8146,7 +8134,7 @@ var require_transport = __commonJS({
8146
8134
  throw new Error("only one of target or targets can be specified");
8147
8135
  }
8148
8136
  if (targets) {
8149
- target = bundlerOverrides["pino-worker"] || join11(__dirname, "worker.js");
8137
+ target = bundlerOverrides["pino-worker"] || join12(__dirname, "worker.js");
8150
8138
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
8151
8139
  return {
8152
8140
  ...dest,
@@ -8164,7 +8152,7 @@ var require_transport = __commonJS({
8164
8152
  });
8165
8153
  });
8166
8154
  } else if (pipeline3) {
8167
- target = bundlerOverrides["pino-worker"] || join11(__dirname, "worker.js");
8155
+ target = bundlerOverrides["pino-worker"] || join12(__dirname, "worker.js");
8168
8156
  options.pipelines = [pipeline3.map((dest) => {
8169
8157
  return {
8170
8158
  ...dest,
@@ -8186,7 +8174,7 @@ var require_transport = __commonJS({
8186
8174
  return origin;
8187
8175
  }
8188
8176
  if (origin === "pino/file") {
8189
- return join11(__dirname, "..", "file.js");
8177
+ return join12(__dirname, "..", "file.js");
8190
8178
  }
8191
8179
  let fixTarget2;
8192
8180
  for (const filePath of callers) {
@@ -8773,7 +8761,7 @@ var require_meta = __commonJS({
8773
8761
  var require_proto = __commonJS({
8774
8762
  "../node_modules/.pnpm/pino@9.14.0/node_modules/pino/lib/proto.js"(exports2, module2) {
8775
8763
  "use strict";
8776
- var { EventEmitter: EventEmitter2 } = require("events");
8764
+ var { EventEmitter: EventEmitter3 } = require("events");
8777
8765
  var {
8778
8766
  lsCacheSym,
8779
8767
  levelValSym,
@@ -8855,7 +8843,7 @@ var require_proto = __commonJS({
8855
8843
  [getLevelSym]: getLevel,
8856
8844
  [setLevelSym]: setLevel
8857
8845
  };
8858
- Object.setPrototypeOf(prototype, EventEmitter2.prototype);
8846
+ Object.setPrototypeOf(prototype, EventEmitter3.prototype);
8859
8847
  module2.exports = function() {
8860
8848
  return Object.create(prototype);
8861
8849
  };
@@ -9176,7 +9164,7 @@ var require_safe_stable_stringify = __commonJS({
9176
9164
  return circularValue;
9177
9165
  }
9178
9166
  let res = "";
9179
- let join11 = ",";
9167
+ let join12 = ",";
9180
9168
  const originalIndentation = indentation;
9181
9169
  if (Array.isArray(value)) {
9182
9170
  if (value.length === 0) {
@@ -9190,7 +9178,7 @@ var require_safe_stable_stringify = __commonJS({
9190
9178
  indentation += spacer;
9191
9179
  res += `
9192
9180
  ${indentation}`;
9193
- join11 = `,
9181
+ join12 = `,
9194
9182
  ${indentation}`;
9195
9183
  }
9196
9184
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -9198,13 +9186,13 @@ ${indentation}`;
9198
9186
  for (; i < maximumValuesToStringify - 1; i++) {
9199
9187
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
9200
9188
  res += tmp2 !== void 0 ? tmp2 : "null";
9201
- res += join11;
9189
+ res += join12;
9202
9190
  }
9203
9191
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
9204
9192
  res += tmp !== void 0 ? tmp : "null";
9205
9193
  if (value.length - 1 > maximumBreadth) {
9206
9194
  const removedKeys = value.length - maximumBreadth - 1;
9207
- res += `${join11}"... ${getItemCount(removedKeys)} not stringified"`;
9195
+ res += `${join12}"... ${getItemCount(removedKeys)} not stringified"`;
9208
9196
  }
9209
9197
  if (spacer !== "") {
9210
9198
  res += `
@@ -9225,7 +9213,7 @@ ${originalIndentation}`;
9225
9213
  let separator = "";
9226
9214
  if (spacer !== "") {
9227
9215
  indentation += spacer;
9228
- join11 = `,
9216
+ join12 = `,
9229
9217
  ${indentation}`;
9230
9218
  whitespace = " ";
9231
9219
  }
@@ -9239,13 +9227,13 @@ ${indentation}`;
9239
9227
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
9240
9228
  if (tmp !== void 0) {
9241
9229
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
9242
- separator = join11;
9230
+ separator = join12;
9243
9231
  }
9244
9232
  }
9245
9233
  if (keyLength > maximumBreadth) {
9246
9234
  const removedKeys = keyLength - maximumBreadth;
9247
9235
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
9248
- separator = join11;
9236
+ separator = join12;
9249
9237
  }
9250
9238
  if (spacer !== "" && separator.length > 1) {
9251
9239
  res = `
@@ -9286,7 +9274,7 @@ ${originalIndentation}`;
9286
9274
  }
9287
9275
  const originalIndentation = indentation;
9288
9276
  let res = "";
9289
- let join11 = ",";
9277
+ let join12 = ",";
9290
9278
  if (Array.isArray(value)) {
9291
9279
  if (value.length === 0) {
9292
9280
  return "[]";
@@ -9299,7 +9287,7 @@ ${originalIndentation}`;
9299
9287
  indentation += spacer;
9300
9288
  res += `
9301
9289
  ${indentation}`;
9302
- join11 = `,
9290
+ join12 = `,
9303
9291
  ${indentation}`;
9304
9292
  }
9305
9293
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -9307,13 +9295,13 @@ ${indentation}`;
9307
9295
  for (; i < maximumValuesToStringify - 1; i++) {
9308
9296
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
9309
9297
  res += tmp2 !== void 0 ? tmp2 : "null";
9310
- res += join11;
9298
+ res += join12;
9311
9299
  }
9312
9300
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
9313
9301
  res += tmp !== void 0 ? tmp : "null";
9314
9302
  if (value.length - 1 > maximumBreadth) {
9315
9303
  const removedKeys = value.length - maximumBreadth - 1;
9316
- res += `${join11}"... ${getItemCount(removedKeys)} not stringified"`;
9304
+ res += `${join12}"... ${getItemCount(removedKeys)} not stringified"`;
9317
9305
  }
9318
9306
  if (spacer !== "") {
9319
9307
  res += `
@@ -9326,7 +9314,7 @@ ${originalIndentation}`;
9326
9314
  let whitespace = "";
9327
9315
  if (spacer !== "") {
9328
9316
  indentation += spacer;
9329
- join11 = `,
9317
+ join12 = `,
9330
9318
  ${indentation}`;
9331
9319
  whitespace = " ";
9332
9320
  }
@@ -9335,7 +9323,7 @@ ${indentation}`;
9335
9323
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
9336
9324
  if (tmp !== void 0) {
9337
9325
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
9338
- separator = join11;
9326
+ separator = join12;
9339
9327
  }
9340
9328
  }
9341
9329
  if (spacer !== "" && separator.length > 1) {
@@ -9393,20 +9381,20 @@ ${originalIndentation}`;
9393
9381
  indentation += spacer;
9394
9382
  let res2 = `
9395
9383
  ${indentation}`;
9396
- const join12 = `,
9384
+ const join13 = `,
9397
9385
  ${indentation}`;
9398
9386
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
9399
9387
  let i = 0;
9400
9388
  for (; i < maximumValuesToStringify - 1; i++) {
9401
9389
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
9402
9390
  res2 += tmp2 !== void 0 ? tmp2 : "null";
9403
- res2 += join12;
9391
+ res2 += join13;
9404
9392
  }
9405
9393
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
9406
9394
  res2 += tmp !== void 0 ? tmp : "null";
9407
9395
  if (value.length - 1 > maximumBreadth) {
9408
9396
  const removedKeys = value.length - maximumBreadth - 1;
9409
- res2 += `${join12}"... ${getItemCount(removedKeys)} not stringified"`;
9397
+ res2 += `${join13}"... ${getItemCount(removedKeys)} not stringified"`;
9410
9398
  }
9411
9399
  res2 += `
9412
9400
  ${originalIndentation}`;
@@ -9422,16 +9410,16 @@ ${originalIndentation}`;
9422
9410
  return '"[Object]"';
9423
9411
  }
9424
9412
  indentation += spacer;
9425
- const join11 = `,
9413
+ const join12 = `,
9426
9414
  ${indentation}`;
9427
9415
  let res = "";
9428
9416
  let separator = "";
9429
9417
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
9430
9418
  if (isTypedArrayWithEntries(value)) {
9431
- res += stringifyTypedArray(value, join11, maximumBreadth);
9419
+ res += stringifyTypedArray(value, join12, maximumBreadth);
9432
9420
  keys = keys.slice(value.length);
9433
9421
  maximumPropertiesToStringify -= value.length;
9434
- separator = join11;
9422
+ separator = join12;
9435
9423
  }
9436
9424
  if (deterministic) {
9437
9425
  keys = sort(keys, comparator);
@@ -9442,13 +9430,13 @@ ${indentation}`;
9442
9430
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
9443
9431
  if (tmp !== void 0) {
9444
9432
  res += `${separator}${strEscape(key2)}: ${tmp}`;
9445
- separator = join11;
9433
+ separator = join12;
9446
9434
  }
9447
9435
  }
9448
9436
  if (keyLength > maximumBreadth) {
9449
9437
  const removedKeys = keyLength - maximumBreadth;
9450
9438
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
9451
- separator = join11;
9439
+ separator = join12;
9452
9440
  }
9453
9441
  if (separator !== "") {
9454
9442
  res = `
@@ -10539,11 +10527,11 @@ var init_lib = __esm({
10539
10527
  }
10540
10528
  }
10541
10529
  },
10542
- addToPath: function addToPath(path60, added, removed, oldPosInc, options) {
10543
- var last = path60.lastComponent;
10530
+ addToPath: function addToPath(path59, added, removed, oldPosInc, options) {
10531
+ var last = path59.lastComponent;
10544
10532
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
10545
10533
  return {
10546
- oldPos: path60.oldPos + oldPosInc,
10534
+ oldPos: path59.oldPos + oldPosInc,
10547
10535
  lastComponent: {
10548
10536
  count: last.count + 1,
10549
10537
  added,
@@ -10553,7 +10541,7 @@ var init_lib = __esm({
10553
10541
  };
10554
10542
  } else {
10555
10543
  return {
10556
- oldPos: path60.oldPos + oldPosInc,
10544
+ oldPos: path59.oldPos + oldPosInc,
10557
10545
  lastComponent: {
10558
10546
  count: 1,
10559
10547
  added,
@@ -10999,10 +10987,10 @@ function attachmentToHistoryMessage(o, ts) {
10999
10987
  const memories = raw.map((m2) => {
11000
10988
  if (!m2 || typeof m2 !== "object") return null;
11001
10989
  const rec = m2;
11002
- const path60 = typeof rec.path === "string" ? rec.path : null;
10990
+ const path59 = typeof rec.path === "string" ? rec.path : null;
11003
10991
  const content = typeof rec.content === "string" ? rec.content : null;
11004
- if (!path60 || content == null) return null;
11005
- const entry = { path: path60, content };
10992
+ if (!path59 || content == null) return null;
10993
+ const entry = { path: path59, content };
11006
10994
  if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
11007
10995
  return entry;
11008
10996
  }).filter((m2) => m2 !== null);
@@ -11833,10 +11821,10 @@ function parseAttachment(obj) {
11833
11821
  const memories = raw.map((m2) => {
11834
11822
  if (!m2 || typeof m2 !== "object") return null;
11835
11823
  const rec = m2;
11836
- const path60 = typeof rec.path === "string" ? rec.path : null;
11824
+ const path59 = typeof rec.path === "string" ? rec.path : null;
11837
11825
  const content = typeof rec.content === "string" ? rec.content : null;
11838
- if (!path60 || content == null) return null;
11839
- const out = { path: path60, content };
11826
+ if (!path59 || content == null) return null;
11827
+ const out = { path: path59, content };
11840
11828
  if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
11841
11829
  return out;
11842
11830
  }).filter((m2) => m2 !== null);
@@ -19330,10 +19318,10 @@ var require_extension = __commonJS({
19330
19318
  var require_websocket = __commonJS({
19331
19319
  "../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket.js"(exports2, module2) {
19332
19320
  "use strict";
19333
- var EventEmitter2 = require("events");
19321
+ var EventEmitter3 = require("events");
19334
19322
  var https = require("https");
19335
- var http2 = require("http");
19336
- var net2 = require("net");
19323
+ var http3 = require("http");
19324
+ var net3 = require("net");
19337
19325
  var tls = require("tls");
19338
19326
  var { randomBytes, createHash: createHash2 } = require("crypto");
19339
19327
  var { Duplex, Readable: Readable3 } = require("stream");
@@ -19362,7 +19350,7 @@ var require_websocket = __commonJS({
19362
19350
  var protocolVersions = [8, 13];
19363
19351
  var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
19364
19352
  var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
19365
- var WebSocket2 = class _WebSocket extends EventEmitter2 {
19353
+ var WebSocket2 = class _WebSocket extends EventEmitter3 {
19366
19354
  /**
19367
19355
  * Create a new `WebSocket`.
19368
19356
  *
@@ -19866,7 +19854,7 @@ var require_websocket = __commonJS({
19866
19854
  }
19867
19855
  const defaultPort = isSecure ? 443 : 80;
19868
19856
  const key = randomBytes(16).toString("base64");
19869
- const request = isSecure ? https.request : http2.request;
19857
+ const request = isSecure ? https.request : http3.request;
19870
19858
  const protocolSet = /* @__PURE__ */ new Set();
19871
19859
  let perMessageDeflate;
19872
19860
  opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
@@ -20067,12 +20055,12 @@ var require_websocket = __commonJS({
20067
20055
  }
20068
20056
  function netConnect(options) {
20069
20057
  options.path = options.socketPath;
20070
- return net2.connect(options);
20058
+ return net3.connect(options);
20071
20059
  }
20072
20060
  function tlsConnect(options) {
20073
20061
  options.path = void 0;
20074
20062
  if (!options.servername && options.servername !== "") {
20075
- options.servername = net2.isIP(options.host) ? "" : options.host;
20063
+ options.servername = net3.isIP(options.host) ? "" : options.host;
20076
20064
  }
20077
20065
  return tls.connect(options);
20078
20066
  }
@@ -20359,8 +20347,8 @@ var require_subprotocol = __commonJS({
20359
20347
  var require_websocket_server = __commonJS({
20360
20348
  "../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket-server.js"(exports2, module2) {
20361
20349
  "use strict";
20362
- var EventEmitter2 = require("events");
20363
- var http2 = require("http");
20350
+ var EventEmitter3 = require("events");
20351
+ var http3 = require("http");
20364
20352
  var { Duplex } = require("stream");
20365
20353
  var { createHash: createHash2 } = require("crypto");
20366
20354
  var extension2 = require_extension();
@@ -20372,7 +20360,7 @@ var require_websocket_server = __commonJS({
20372
20360
  var RUNNING = 0;
20373
20361
  var CLOSING = 1;
20374
20362
  var CLOSED = 2;
20375
- var WebSocketServer2 = class extends EventEmitter2 {
20363
+ var WebSocketServer2 = class extends EventEmitter3 {
20376
20364
  /**
20377
20365
  * Create a `WebSocketServer` instance.
20378
20366
  *
@@ -20435,8 +20423,8 @@ var require_websocket_server = __commonJS({
20435
20423
  );
20436
20424
  }
20437
20425
  if (options.port != null) {
20438
- this._server = http2.createServer((req, res) => {
20439
- const body = http2.STATUS_CODES[426];
20426
+ this._server = http3.createServer((req, res) => {
20427
+ const body = http3.STATUS_CODES[426];
20440
20428
  res.writeHead(426, {
20441
20429
  "Content-Length": body.length,
20442
20430
  "Content-Type": "text/plain"
@@ -20723,7 +20711,7 @@ var require_websocket_server = __commonJS({
20723
20711
  this.destroy();
20724
20712
  }
20725
20713
  function abortHandshake(socket, code, message, headers) {
20726
- message = message || http2.STATUS_CODES[code];
20714
+ message = message || http3.STATUS_CODES[code];
20727
20715
  headers = {
20728
20716
  Connection: "close",
20729
20717
  "Content-Type": "text/html",
@@ -20732,7 +20720,7 @@ var require_websocket_server = __commonJS({
20732
20720
  };
20733
20721
  socket.once("finish", socket.destroy);
20734
20722
  socket.end(
20735
- `HTTP/1.1 ${code} ${http2.STATUS_CODES[code]}\r
20723
+ `HTTP/1.1 ${code} ${http3.STATUS_CODES[code]}\r
20736
20724
  ` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
20737
20725
  );
20738
20726
  }
@@ -21035,7 +21023,7 @@ var require_BufferList = __commonJS({
21035
21023
  this.head = this.tail = null;
21036
21024
  this.length = 0;
21037
21025
  };
21038
- BufferList.prototype.join = function join11(s) {
21026
+ BufferList.prototype.join = function join12(s) {
21039
21027
  if (this.length === 0) return "";
21040
21028
  var p2 = this.head;
21041
21029
  var ret = "" + p2.data;
@@ -23625,8 +23613,8 @@ var require_utils = __commonJS({
23625
23613
  var result = transform[inputType][outputType](input);
23626
23614
  return result;
23627
23615
  };
23628
- exports2.resolve = function(path60) {
23629
- var parts = path60.split("/");
23616
+ exports2.resolve = function(path59) {
23617
+ var parts = path59.split("/");
23630
23618
  var result = [];
23631
23619
  for (var index = 0; index < parts.length; index++) {
23632
23620
  var part = parts[index];
@@ -29479,18 +29467,18 @@ var require_object = __commonJS({
29479
29467
  var object = new ZipObject(name, zipObjectContent, o);
29480
29468
  this.files[name] = object;
29481
29469
  };
29482
- var parentFolder = function(path60) {
29483
- if (path60.slice(-1) === "/") {
29484
- path60 = path60.substring(0, path60.length - 1);
29470
+ var parentFolder = function(path59) {
29471
+ if (path59.slice(-1) === "/") {
29472
+ path59 = path59.substring(0, path59.length - 1);
29485
29473
  }
29486
- var lastSlash = path60.lastIndexOf("/");
29487
- return lastSlash > 0 ? path60.substring(0, lastSlash) : "";
29474
+ var lastSlash = path59.lastIndexOf("/");
29475
+ return lastSlash > 0 ? path59.substring(0, lastSlash) : "";
29488
29476
  };
29489
- var forceTrailingSlash = function(path60) {
29490
- if (path60.slice(-1) !== "/") {
29491
- path60 += "/";
29477
+ var forceTrailingSlash = function(path59) {
29478
+ if (path59.slice(-1) !== "/") {
29479
+ path59 += "/";
29492
29480
  }
29493
- return path60;
29481
+ return path59;
29494
29482
  };
29495
29483
  var folderAdd = function(name, createFolders) {
29496
29484
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -30945,6 +30933,8 @@ function resolveConfig(opts) {
30945
30933
  const frpcBinary = env.CLAWD_FRPC_BIN ?? null;
30946
30934
  const rawMode = env.CLAWD_CC_MODE;
30947
30935
  const mode = DAEMON_MODE_VALUES.includes(rawMode ?? "") ? rawMode : "sdk";
30936
+ const filePreviewPorts = Array.isArray(fileCfg.previewPorts) ? fileCfg.previewPorts.map((n) => Number(n)).filter((n) => Number.isInteger(n) && n > 0) : null;
30937
+ 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;
30948
30938
  return {
30949
30939
  port,
30950
30940
  host,
@@ -30956,7 +30946,8 @@ function resolveConfig(opts) {
30956
30946
  authToken,
30957
30947
  noTunnelPersist,
30958
30948
  frpcBinary,
30959
- mode
30949
+ mode,
30950
+ previewPorts
30960
30951
  };
30961
30952
  }
30962
30953
  var HELP_TEXT = `clawd [options]
@@ -32995,6 +32986,7 @@ var SessionManager = class {
32995
32986
  iconKey: args.iconKey,
32996
32987
  forkedFromSessionId: args.forkedFromSessionId,
32997
32988
  ownerPersonaId: args.ownerPersonaId,
32989
+ appBuilderProject: args.appBuilderProject,
32998
32990
  creatorPrincipalId,
32999
32991
  ...creatorOwnerPrincipalId ? { creatorOwnerPrincipalId } : {},
33000
32992
  createdAt: iso,
@@ -34631,6 +34623,15 @@ var DEFAULT_PERSONAS = [
34631
34623
  model: "opus",
34632
34624
  iconKey: "assist",
34633
34625
  public: false
34626
+ },
34627
+ {
34628
+ // app-builder:全栈应用搭建师,绑 extension-kit 模板 + multi-project picker
34629
+ // spec: superpowers/specs/2026-06-01-app-builder-project-picker-design.md
34630
+ personaId: "persona-app-builder",
34631
+ label: "App Builder",
34632
+ model: "opus",
34633
+ iconKey: "assist",
34634
+ public: false
34634
34635
  }
34635
34636
  ];
34636
34637
  function findDefaultsRoot() {
@@ -36327,13 +36328,63 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
36327
36328
  var wrapper_default = import_websocket.default;
36328
36329
 
36329
36330
  // src/transport/local-ws-server.ts
36331
+ var import_node_http2 = __toESM(require("http"), 1);
36332
+
36333
+ // src/transport/preview-proxy.ts
36330
36334
  var import_node_http = __toESM(require("http"), 1);
36335
+ var import_node_net = __toESM(require("net"), 1);
36336
+ var PREVIEW_RE = /^\/preview\/(\d+)(?:\/.*)?$/;
36337
+ function matchPreviewPort(pathname) {
36338
+ const m2 = pathname.match(PREVIEW_RE);
36339
+ return m2 ? Number(m2[1]) : null;
36340
+ }
36341
+ function isPreviewPortAllowed(port, cfg) {
36342
+ return cfg.allowedPorts.includes(port);
36343
+ }
36344
+ function proxyPreviewHttp(req, res, port) {
36345
+ return new Promise((resolve6) => {
36346
+ const upstream = import_node_http.default.request(
36347
+ { host: "localhost", port, method: req.method, path: req.url, headers: req.headers },
36348
+ (upRes) => {
36349
+ res.writeHead(upRes.statusCode || 502, upRes.headers);
36350
+ upRes.pipe(res);
36351
+ upRes.on("end", () => resolve6());
36352
+ }
36353
+ );
36354
+ upstream.on("error", (e) => {
36355
+ if (!res.headersSent) {
36356
+ res.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
36357
+ }
36358
+ res.end(`preview upstream error: ${e.message}`);
36359
+ resolve6();
36360
+ });
36361
+ req.pipe(upstream);
36362
+ });
36363
+ }
36364
+ function proxyPreviewUpgrade(req, socket, head, port) {
36365
+ const upstream = import_node_net.default.connect(port, "localhost", () => {
36366
+ let raw = `${req.method} ${req.url} HTTP/1.1\r
36367
+ `;
36368
+ for (let i = 0; i < req.rawHeaders.length; i += 2) {
36369
+ raw += `${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r
36370
+ `;
36371
+ }
36372
+ raw += "\r\n";
36373
+ upstream.write(raw);
36374
+ if (head && head.length) upstream.write(head);
36375
+ upstream.pipe(socket);
36376
+ socket.pipe(upstream);
36377
+ });
36378
+ upstream.on("error", () => socket.destroy());
36379
+ socket.on("error", () => upstream.destroy());
36380
+ }
36381
+
36382
+ // src/transport/local-ws-server.ts
36331
36383
  var LocalWsServer = class {
36332
36384
  constructor(opts) {
36333
36385
  this.opts = opts;
36334
36386
  this.logger = opts.logger;
36335
36387
  this.pingIntervalMs = opts.pingIntervalMs ?? 3e4;
36336
- this.currentOwnerPrincipalId = opts.ownerPrincipalId;
36337
36388
  }
36338
36389
  opts;
36339
36390
  wss = null;
@@ -36345,19 +36396,28 @@ var LocalWsServer = class {
36345
36396
  capabilityIdToClients = /* @__PURE__ */ new Map();
36346
36397
  logger;
36347
36398
  pingIntervalMs;
36348
- // 飞书热切换(spec 决策 #9,2026-06-01 拍板):登录后实时更新,broadcastToPrincipal
36349
- // 路由用最新身份;初始值来自 opts.ownerPrincipalId
36350
- currentOwnerPrincipalId;
36351
- /** 飞书热切换:登录回调成功后由 daemon wiring 调用,更新 DM 路由身份 */
36352
- setOwnerPrincipalId(id) {
36353
- this.currentOwnerPrincipalId = id;
36354
- }
36355
36399
  async start() {
36356
36400
  const host = this.opts.host ?? "127.0.0.1";
36357
36401
  await new Promise((resolve6, reject) => {
36358
- const httpServer = import_node_http.default.createServer((req, res) => this.handleHttpRequest(req, res));
36402
+ const httpServer = import_node_http2.default.createServer((req, res) => this.handleHttpRequest(req, res));
36359
36403
  const wss = new import_websocket_server.default({ noServer: true, clientTracking: true });
36360
36404
  httpServer.on("upgrade", (req, socket, head) => {
36405
+ if (req.url?.startsWith("/preview/")) {
36406
+ const pathname = (() => {
36407
+ try {
36408
+ return new URL(req.url, "http://localhost").pathname;
36409
+ } catch {
36410
+ return "/";
36411
+ }
36412
+ })();
36413
+ const port = matchPreviewPort(pathname);
36414
+ if (port != null && (this.opts.previewPorts ?? []).includes(port)) {
36415
+ proxyPreviewUpgrade(req, socket, head, port);
36416
+ } else {
36417
+ socket.destroy();
36418
+ }
36419
+ return;
36420
+ }
36361
36421
  wss.handleUpgrade(req, socket, head, (ws) => {
36362
36422
  this.routeConnection(ws, req);
36363
36423
  });
@@ -36471,7 +36531,7 @@ var LocalWsServer = class {
36471
36531
  // principalId === 'cap_xxx' → 该 capability 的所有 guest ws (多端 / 多 tab)
36472
36532
  // 用于 DM inbox:event 路由: from + to 两侧各播一次, 让双方 UI 实时看到 thread 更新.
36473
36533
  broadcastToPrincipal(principalId, frame) {
36474
- if (principalId === "owner" || principalId === this.currentOwnerPrincipalId) {
36534
+ if (principalId === "owner" || principalId === this.opts.ownerPrincipalId) {
36475
36535
  this.broadcastToOwners(frame);
36476
36536
  return;
36477
36537
  }
@@ -36523,21 +36583,6 @@ var LocalWsServer = class {
36523
36583
  getClientContext(clientId) {
36524
36584
  return this.clients.get(clientId)?.ctx ?? null;
36525
36585
  }
36526
- // 飞书热切换(spec 决策 #9):身份切换后踢掉所有连接强制重连——在线连接的 ctx
36527
- // (attachClientContext 时绑定)持有旧身份,重连后用新身份重建。
36528
- // close code 4408(非 4401!4401 = token 撤销,客户端会停止重连):
36529
- // 客户端按普通断连处理 → 自动重连 → 新身份 ctx。
36530
- closeAllClients(code = 4408, reason = "IDENTITY_SWITCHED") {
36531
- const ids = [...this.clients.keys()];
36532
- for (const id of ids) {
36533
- const c = this.clients.get(id);
36534
- if (!c) continue;
36535
- try {
36536
- c.ws.close(code, reason);
36537
- } catch {
36538
- }
36539
- }
36540
- }
36541
36586
  // Task 1.10 撤销级联:关闭某 capability 的所有活跃 ws 连接。close code 4401
36542
36587
  // 让客户端识别 'capability revoked' 而非普通断连。
36543
36588
  closeConnectionsByCapability(capabilityId, code = 4401, reason = "TOKEN_REVOKED") {
@@ -38020,23 +38065,13 @@ function createHttpRouter(deps) {
38020
38065
  });
38021
38066
  return true;
38022
38067
  }
38023
- if (url.pathname === "/auth/callback") {
38024
- if (req.method !== "GET") {
38025
- sendJson(res, 404, { code: "NOT_FOUND", message: `no route for ${req.method} ${url.pathname}` });
38026
- return true;
38027
- }
38028
- if (!deps.feishuLogin) {
38029
- sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "feishu login not wired" });
38068
+ if (url.pathname.startsWith("/preview/")) {
38069
+ const port = matchPreviewPort(url.pathname);
38070
+ if (port != null && isPreviewPortAllowed(port, { allowedPorts: deps.previewPorts ?? [] })) {
38071
+ await proxyPreviewHttp(req, res, port);
38030
38072
  return true;
38031
38073
  }
38032
- const result = await deps.feishuLogin.handleLoginCallback({
38033
- token: url.searchParams.get("token") ?? "",
38034
- state: url.searchParams.get("state") ?? "",
38035
- expiresAt: url.searchParams.get("expires_at")
38036
- });
38037
- const html = result.ok ? `<!doctype html><meta charset="utf-8"><title>clawd</title><style>body{font-family:-apple-system,sans-serif;padding:48px;color:#222}</style><h2>\u767B\u5F55\u6210\u529F\uFF0C\u53EF\u5173\u95ED\u6B64\u9875\u3002</h2><p>\u8EAB\u4EFD\uFF1A${escapeHtml(result.identity.displayName)}</p><script>setTimeout(()=>window.close(),1500)</script>` : `<!doctype html><meta charset="utf-8"><title>clawd</title><style>body{font-family:-apple-system,sans-serif;padding:48px;color:#922}</style><h2>\u767B\u5F55\u5931\u8D25</h2><p>${escapeHtml(result.reason)}</p>`;
38038
- res.writeHead(result.ok ? 200 : 400, { "Content-Type": "text/html; charset=utf-8" });
38039
- res.end(html);
38074
+ sendJson(res, 403, { code: "PREVIEW_PORT_FORBIDDEN", message: "preview port not allowed" });
38040
38075
  return true;
38041
38076
  }
38042
38077
  if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/") && url.pathname !== "/attachments/upload" && url.pathname !== "/extensions/import") {
@@ -38274,9 +38309,6 @@ function parseUrl(rawUrl) {
38274
38309
  return null;
38275
38310
  }
38276
38311
  }
38277
- function escapeHtml(s) {
38278
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
38279
- }
38280
38312
  function sendJson(res, status, body, extraHeaders) {
38281
38313
  res.writeHead(status, {
38282
38314
  "Content-Type": "application/json; charset=utf-8",
@@ -39299,160 +39331,6 @@ function loadOwnerDisplayName(dataDir) {
39299
39331
  return displayName;
39300
39332
  }
39301
39333
 
39302
- // src/feishu-auth/owner-identity-store.ts
39303
- var import_node_fs27 = __toESM(require("fs"), 1);
39304
- var import_node_path31 = __toESM(require("path"), 1);
39305
- var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
39306
- var OwnerIdentityStore = class {
39307
- file;
39308
- constructor(dataDir) {
39309
- this.file = import_node_path31.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
39310
- }
39311
- read() {
39312
- let raw;
39313
- try {
39314
- raw = import_node_fs27.default.readFileSync(this.file, "utf8");
39315
- } catch {
39316
- return null;
39317
- }
39318
- let parsed;
39319
- try {
39320
- parsed = JSON.parse(raw);
39321
- } catch {
39322
- return null;
39323
- }
39324
- const r = parsed;
39325
- if (!r.identity || typeof r.identity.unionId !== "string" || r.identity.unionId.length === 0 || typeof r.identity.displayName !== "string" || r.identity.displayName.length === 0 || typeof r.ttcToken !== "string" || r.ttcToken.length === 0) {
39326
- return null;
39327
- }
39328
- return {
39329
- identity: {
39330
- unionId: r.identity.unionId,
39331
- displayName: r.identity.displayName,
39332
- ...typeof r.identity.avatarUrl === "string" ? { avatarUrl: r.identity.avatarUrl } : {}
39333
- },
39334
- ttcToken: r.ttcToken,
39335
- ttcTokenExpiresAt: typeof r.ttcTokenExpiresAt === "number" ? r.ttcTokenExpiresAt : null,
39336
- loginAt: typeof r.loginAt === "string" ? r.loginAt : (/* @__PURE__ */ new Date(0)).toISOString()
39337
- };
39338
- }
39339
- write(record) {
39340
- import_node_fs27.default.mkdirSync(import_node_path31.default.dirname(this.file), { recursive: true });
39341
- import_node_fs27.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
39342
- try {
39343
- import_node_fs27.default.chmodSync(this.file, 384);
39344
- } catch {
39345
- }
39346
- }
39347
- clear() {
39348
- try {
39349
- import_node_fs27.default.unlinkSync(this.file);
39350
- } catch (err) {
39351
- const code = err?.code;
39352
- if (code !== "ENOENT") throw err;
39353
- }
39354
- }
39355
- };
39356
-
39357
- // src/feishu-auth/login-flow.ts
39358
- var import_node_crypto11 = __toESM(require("crypto"), 1);
39359
- var STATE_TTL_MS = 5 * 60 * 1e3;
39360
- var LoginFlow = class {
39361
- constructor(deps) {
39362
- this.deps = deps;
39363
- }
39364
- deps;
39365
- pendingStates = /* @__PURE__ */ new Map();
39366
- start() {
39367
- const state = import_node_crypto11.default.randomBytes(16).toString("base64url");
39368
- const now = (this.deps.now ?? Date.now)();
39369
- this.pendingStates.set(state, now);
39370
- this.gcExpired(now);
39371
- const url = new URL("/auth/authorize", this.deps.ttcAuthBase);
39372
- url.searchParams.set("callback_url", this.deps.getCallbackUrl());
39373
- url.searchParams.set("state", state);
39374
- url.searchParams.set("auto", "1");
39375
- return { authUrl: url.toString(), state };
39376
- }
39377
- async handleCallback(params) {
39378
- const now = (this.deps.now ?? Date.now)();
39379
- const issuedAt = this.pendingStates.get(params.state);
39380
- if (issuedAt === void 0) {
39381
- return { ok: false, reason: "state mismatch" };
39382
- }
39383
- this.pendingStates.delete(params.state);
39384
- if (now - issuedAt > STATE_TTL_MS) {
39385
- return { ok: false, reason: "state expired" };
39386
- }
39387
- if (!params.token) {
39388
- return { ok: false, reason: "missing token" };
39389
- }
39390
- let identity;
39391
- try {
39392
- identity = await this.deps.fetchUserInfo(params.token);
39393
- } catch (err) {
39394
- return { ok: false, reason: `user_info failed: ${err.message}` };
39395
- }
39396
- const expiresAtNum = params.expiresAt ? Number.parseInt(params.expiresAt, 10) : NaN;
39397
- this.deps.store.write({
39398
- identity,
39399
- ttcToken: params.token,
39400
- ttcTokenExpiresAt: Number.isFinite(expiresAtNum) ? expiresAtNum : null,
39401
- loginAt: new Date(now).toISOString()
39402
- });
39403
- return { ok: true, identity };
39404
- }
39405
- gcExpired(now) {
39406
- for (const [state, issuedAt] of this.pendingStates) {
39407
- if (now - issuedAt > STATE_TTL_MS) this.pendingStates.delete(state);
39408
- }
39409
- }
39410
- };
39411
-
39412
- // src/feishu-auth/ttc-client.ts
39413
- var TTC_HOSTS = {
39414
- auth: "https://app.ttcadvisory.com",
39415
- api: "https://api.ttcadvisory.com"
39416
- };
39417
- var TtcUserInfoError = class extends Error {
39418
- constructor(code, message) {
39419
- super(message);
39420
- this.code = code;
39421
- this.name = "TtcUserInfoError";
39422
- }
39423
- code;
39424
- };
39425
- async function fetchTtcUserInfo(opts) {
39426
- const doFetch = opts.fetchImpl ?? fetch;
39427
- let res;
39428
- try {
39429
- res = await doFetch(`${opts.apiBase}/api/user_service/v1/login/user`, {
39430
- headers: { Authorization: `Bearer ${opts.token}` }
39431
- });
39432
- } catch (err) {
39433
- throw new TtcUserInfoError("NETWORK", `TTC user_info request failed: ${err.message}`);
39434
- }
39435
- if (!res.ok) {
39436
- if (res.status === 401) {
39437
- throw new TtcUserInfoError("UNAUTHORIZED", "TTC token invalid or expired");
39438
- }
39439
- throw new TtcUserInfoError("API_ERROR", `TTC user_info HTTP ${res.status}`);
39440
- }
39441
- const body = await res.json();
39442
- if (body.code !== 0) {
39443
- throw new TtcUserInfoError("API_ERROR", `TTC user_info code=${body.code}: ${body.message ?? ""}`);
39444
- }
39445
- const d = body.data;
39446
- if (!d || typeof d.union_id !== "string" || d.union_id.length === 0 || typeof d.name !== "string" || d.name.length === 0) {
39447
- throw new TtcUserInfoError("BAD_RESPONSE", "TTC user_info missing union_id or name");
39448
- }
39449
- return {
39450
- unionId: d.union_id,
39451
- displayName: d.name,
39452
- ...typeof d.avatar_url === "string" && d.avatar_url.length > 0 ? { avatarUrl: d.avatar_url } : {}
39453
- };
39454
- }
39455
-
39456
39334
  // src/index.ts
39457
39335
  init_protocol();
39458
39336
 
@@ -39460,12 +39338,12 @@ init_protocol();
39460
39338
  init_protocol();
39461
39339
 
39462
39340
  // src/session/fork.ts
39463
- var import_node_fs28 = __toESM(require("fs"), 1);
39341
+ var import_node_fs27 = __toESM(require("fs"), 1);
39464
39342
  var import_node_os14 = __toESM(require("os"), 1);
39465
- var import_node_path32 = __toESM(require("path"), 1);
39343
+ var import_node_path31 = __toESM(require("path"), 1);
39466
39344
  init_claude_history();
39467
39345
  function readJsonlEntries(file) {
39468
- const raw = import_node_fs28.default.readFileSync(file, "utf8");
39346
+ const raw = import_node_fs27.default.readFileSync(file, "utf8");
39469
39347
  const out = [];
39470
39348
  for (const line of raw.split("\n")) {
39471
39349
  const t = line.trim();
@@ -39478,10 +39356,10 @@ function readJsonlEntries(file) {
39478
39356
  return out;
39479
39357
  }
39480
39358
  function forkSession(input) {
39481
- const baseDir = input.baseDir ?? import_node_path32.default.join(import_node_os14.default.homedir(), ".claude");
39482
- const projectDir = import_node_path32.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
39483
- const sourceFile = import_node_path32.default.join(projectDir, `${input.toolSessionId}.jsonl`);
39484
- if (!import_node_fs28.default.existsSync(sourceFile)) {
39359
+ const baseDir = input.baseDir ?? import_node_path31.default.join(import_node_os14.default.homedir(), ".claude");
39360
+ const projectDir = import_node_path31.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
39361
+ const sourceFile = import_node_path31.default.join(projectDir, `${input.toolSessionId}.jsonl`);
39362
+ if (!import_node_fs27.default.existsSync(sourceFile)) {
39485
39363
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
39486
39364
  }
39487
39365
  const entries = readJsonlEntries(sourceFile);
@@ -39511,9 +39389,9 @@ function forkSession(input) {
39511
39389
  }
39512
39390
  forkedLines.push(JSON.stringify(forked));
39513
39391
  }
39514
- const forkedFilePath = import_node_path32.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
39515
- import_node_fs28.default.mkdirSync(projectDir, { recursive: true });
39516
- import_node_fs28.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
39392
+ const forkedFilePath = import_node_path31.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
39393
+ import_node_fs27.default.mkdirSync(projectDir, { recursive: true });
39394
+ import_node_fs27.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
39517
39395
  return { forkedToolSessionId, forkedFilePath };
39518
39396
  }
39519
39397
 
@@ -39613,7 +39491,34 @@ function buildSessionHandlers(deps) {
39613
39491
  const update = async (frame, _client, ctx) => {
39614
39492
  const args = SessionUpdateArgs.parse(frame);
39615
39493
  ensureSessionAccess(ctx, args.sessionId, "send");
39494
+ const prevFile = manager.findOwnedSession(args.sessionId);
39495
+ const prevProject = prevFile?.appBuilderProject ?? null;
39496
+ const nextProject = args.patch.appBuilderProject ?? prevProject;
39616
39497
  const { response, broadcast } = manager.update(args);
39498
+ if (deps.devServerLifecycle && deps.appBuilderStore && args.patch.appBuilderProject !== void 0) {
39499
+ const lifecycle = deps.devServerLifecycle;
39500
+ const projectStore = deps.appBuilderStore;
39501
+ const tunnelUrl = deps.getTunnelUrl?.() ?? null;
39502
+ try {
39503
+ if (prevProject && prevProject !== nextProject) {
39504
+ await lifecycle.stopForSession(args.sessionId);
39505
+ }
39506
+ if (nextProject && nextProject !== "" && nextProject !== prevProject) {
39507
+ const projects = await projectStore.list();
39508
+ const target = projects.find((p2) => p2.name === nextProject);
39509
+ if (target) {
39510
+ lifecycle.startForSession({
39511
+ sessionId: args.sessionId,
39512
+ projectName: nextProject,
39513
+ cwd: projectStore.projectDir(nextProject),
39514
+ port: target.port,
39515
+ tunnelHost: tunnelUrl ? new URL(tunnelUrl.replace(/^wss?:\/\//i, "https://")).host : null
39516
+ });
39517
+ }
39518
+ }
39519
+ } catch {
39520
+ }
39521
+ }
39617
39522
  return { response: { type: "session:info", ...response }, broadcast };
39618
39523
  };
39619
39524
  const del = async (frame, _client, ctx) => {
@@ -39865,7 +39770,7 @@ function buildPermissionHandlers(deps) {
39865
39770
  }
39866
39771
 
39867
39772
  // src/handlers/history.ts
39868
- var path42 = __toESM(require("path"), 1);
39773
+ var path41 = __toESM(require("path"), 1);
39869
39774
  init_protocol();
39870
39775
 
39871
39776
  // src/session/recent-dirs.ts
@@ -39883,7 +39788,7 @@ function listRecentDirs(store, limit = 50) {
39883
39788
  }
39884
39789
 
39885
39790
  // src/permission/persona-paths.ts
39886
- var path41 = __toESM(require("path"), 1);
39791
+ var path40 = __toESM(require("path"), 1);
39887
39792
  function getAllowedPersonaIds(grants, action) {
39888
39793
  const ids = /* @__PURE__ */ new Set();
39889
39794
  for (const g2 of grants) {
@@ -39896,26 +39801,26 @@ function getAllowedPersonaIds(grants, action) {
39896
39801
  return ids;
39897
39802
  }
39898
39803
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read") {
39899
- const root = path41.resolve(personaRoot);
39900
- const target = path41.resolve(absPath);
39901
- const sep3 = root.endsWith(path41.sep) ? "" : path41.sep;
39804
+ const root = path40.resolve(personaRoot);
39805
+ const target = path40.resolve(absPath);
39806
+ const sep3 = root.endsWith(path40.sep) ? "" : path40.sep;
39902
39807
  if (!target.startsWith(root + sep3)) return false;
39903
- const rel = path41.relative(root, target);
39808
+ const rel = path40.relative(root, target);
39904
39809
  if (!rel || rel.startsWith("..")) return false;
39905
- const personaId = rel.split(path41.sep)[0];
39810
+ const personaId = rel.split(path40.sep)[0];
39906
39811
  if (!personaId) return false;
39907
39812
  const allowed = getAllowedPersonaIds(grants, action);
39908
39813
  if (allowed === "*") return true;
39909
39814
  return allowed.has(personaId);
39910
39815
  }
39911
39816
  function personaIdFromPath(absPath, personaRoot) {
39912
- const root = path41.resolve(personaRoot);
39913
- const target = path41.resolve(absPath);
39914
- const sep3 = root.endsWith(path41.sep) ? "" : path41.sep;
39817
+ const root = path40.resolve(personaRoot);
39818
+ const target = path40.resolve(absPath);
39819
+ const sep3 = root.endsWith(path40.sep) ? "" : path40.sep;
39915
39820
  if (!target.startsWith(root + sep3)) return null;
39916
- const rel = path41.relative(root, target);
39821
+ const rel = path40.relative(root, target);
39917
39822
  if (!rel || rel.startsWith("..")) return null;
39918
- const id = rel.split(path41.sep)[0];
39823
+ const id = rel.split(path40.sep)[0];
39919
39824
  return id || null;
39920
39825
  }
39921
39826
 
@@ -39940,7 +39845,7 @@ function buildHistoryHandlers(deps) {
39940
39845
  if (!pid) return false;
39941
39846
  return isGuestPathAllowed(
39942
39847
  ctx.grants,
39943
- path42.join(personaRoot, pid),
39848
+ path41.join(personaRoot, pid),
39944
39849
  personaRoot,
39945
39850
  "read"
39946
39851
  );
@@ -39951,7 +39856,7 @@ function buildHistoryHandlers(deps) {
39951
39856
  };
39952
39857
  const list = async (frame, _client, ctx) => {
39953
39858
  const args = HistoryListArgs.parse(frame);
39954
- assertGuestPath(ctx, path42.resolve(args.projectPath), personaRoot, "history:list");
39859
+ assertGuestPath(ctx, path41.resolve(args.projectPath), personaRoot, "history:list");
39955
39860
  const sessions = await history.listSessions(args);
39956
39861
  return { response: { type: "history:list", sessions } };
39957
39862
  };
@@ -39983,13 +39888,13 @@ function buildHistoryHandlers(deps) {
39983
39888
  };
39984
39889
  const subagents = async (frame, _client, ctx) => {
39985
39890
  const args = HistorySubagentsArgs.parse(frame);
39986
- assertGuestPath(ctx, path42.resolve(args.cwd), personaRoot, "history:subagents");
39891
+ assertGuestPath(ctx, path41.resolve(args.cwd), personaRoot, "history:subagents");
39987
39892
  const subs = await history.listSubagents(args);
39988
39893
  return { response: { type: "history:subagents", subagents: subs } };
39989
39894
  };
39990
39895
  const subagentRead = async (frame, _client, ctx) => {
39991
39896
  const args = HistorySubagentReadArgs.parse(frame);
39992
- assertGuestPath(ctx, path42.resolve(args.cwd), personaRoot, "history:subagent-read");
39897
+ assertGuestPath(ctx, path41.resolve(args.cwd), personaRoot, "history:subagent-read");
39993
39898
  const res = await history.readSubagent(args);
39994
39899
  return { response: { type: "history:subagent-read", ...res } };
39995
39900
  };
@@ -39997,7 +39902,7 @@ function buildHistoryHandlers(deps) {
39997
39902
  const dirs = listRecentDirs(store);
39998
39903
  if (ctx?.principal.kind === "guest" && personaRoot) {
39999
39904
  const filtered = dirs.filter(
40000
- (d) => isGuestPathAllowed(ctx.grants, path42.resolve(d.cwd), personaRoot, "read")
39905
+ (d) => isGuestPathAllowed(ctx.grants, path41.resolve(d.cwd), personaRoot, "read")
40001
39906
  );
40002
39907
  return { response: { type: "history:recentDirs", dirs: filtered } };
40003
39908
  }
@@ -40014,7 +39919,7 @@ function buildHistoryHandlers(deps) {
40014
39919
  }
40015
39920
 
40016
39921
  // src/handlers/workspace.ts
40017
- var path43 = __toESM(require("path"), 1);
39922
+ var path42 = __toESM(require("path"), 1);
40018
39923
  var os15 = __toESM(require("os"), 1);
40019
39924
  init_protocol();
40020
39925
  init_protocol();
@@ -40055,22 +39960,22 @@ function buildWorkspaceHandlers(deps) {
40055
39960
  const args = WorkspaceListArgs.parse(frame);
40056
39961
  const isGuest = ctx?.principal.kind === "guest";
40057
39962
  const fallbackCwd = isGuest && personaRoot ? personaRoot : os15.homedir();
40058
- const resolvedCwd = path43.resolve(args.cwd ?? fallbackCwd);
40059
- const target = args.path ? path43.resolve(resolvedCwd, args.path) : resolvedCwd;
39963
+ const resolvedCwd = path42.resolve(args.cwd ?? fallbackCwd);
39964
+ const target = args.path ? path42.resolve(resolvedCwd, args.path) : resolvedCwd;
40060
39965
  assertGuestPath2(ctx, target, personaRoot, "workspace:list");
40061
39966
  const res = workspace.list({ ...args, cwd: resolvedCwd });
40062
39967
  return { response: { type: "workspace:list", ...res } };
40063
39968
  };
40064
39969
  const read = async (frame, _client, ctx) => {
40065
39970
  const args = WorkspaceReadArgs.parse(frame);
40066
- const target = path43.isAbsolute(args.path) ? path43.resolve(args.path) : path43.resolve(args.cwd, args.path);
39971
+ const target = path42.isAbsolute(args.path) ? path42.resolve(args.path) : path42.resolve(args.cwd, args.path);
40067
39972
  assertGuestPath2(ctx, target, personaRoot, "workspace:read");
40068
39973
  const res = workspace.read(args);
40069
39974
  return { response: { type: "workspace:read", ...res } };
40070
39975
  };
40071
39976
  const skillsList = async (frame, _client, ctx) => {
40072
39977
  const args = SkillsListArgs.parse(frame);
40073
- const cwdAbs = path43.resolve(args.cwd);
39978
+ const cwdAbs = path42.resolve(args.cwd);
40074
39979
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list");
40075
39980
  const list2 = skills.list(args);
40076
39981
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -40082,7 +39987,7 @@ function buildWorkspaceHandlers(deps) {
40082
39987
  };
40083
39988
  const agentsList = async (frame, _client, ctx) => {
40084
39989
  const args = AgentsListArgs.parse(frame);
40085
- const cwdAbs = path43.resolve(args.cwd);
39990
+ const cwdAbs = path42.resolve(args.cwd);
40086
39991
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list");
40087
39992
  const list2 = agents.list(args);
40088
39993
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -40101,20 +40006,20 @@ function buildWorkspaceHandlers(deps) {
40101
40006
  }
40102
40007
 
40103
40008
  // src/handlers/git.ts
40104
- var path45 = __toESM(require("path"), 1);
40009
+ var path44 = __toESM(require("path"), 1);
40105
40010
  init_protocol();
40106
40011
  init_protocol();
40107
40012
 
40108
40013
  // src/workspace/git.ts
40109
40014
  var import_node_child_process6 = require("child_process");
40110
- var import_node_fs29 = __toESM(require("fs"), 1);
40111
- var import_node_path33 = __toESM(require("path"), 1);
40015
+ var import_node_fs28 = __toESM(require("fs"), 1);
40016
+ var import_node_path32 = __toESM(require("path"), 1);
40112
40017
  var import_node_util = require("util");
40113
40018
  var pexec = (0, import_node_util.promisify)(import_node_child_process6.execFile);
40114
40019
  function normalizePath(p2) {
40115
- const resolved = import_node_path33.default.resolve(p2);
40020
+ const resolved = import_node_path32.default.resolve(p2);
40116
40021
  try {
40117
- return import_node_fs29.default.realpathSync(resolved);
40022
+ return import_node_fs28.default.realpathSync(resolved);
40118
40023
  } catch {
40119
40024
  return resolved;
40120
40025
  }
@@ -40187,7 +40092,7 @@ async function listGitBranches(cwd) {
40187
40092
  // src/handlers/git.ts
40188
40093
  function assertGuestCwd(ctx, cwd, personaRoot, method) {
40189
40094
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
40190
- if (!isGuestPathAllowed(ctx.grants, path45.resolve(cwd), personaRoot, "read")) {
40095
+ if (!isGuestPathAllowed(ctx.grants, path44.resolve(cwd), personaRoot, "read")) {
40191
40096
  throw new ClawdError(
40192
40097
  ERROR_CODES.UNAUTHORIZED,
40193
40098
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -40549,36 +40454,6 @@ function buildWhoamiHandler(deps) {
40549
40454
  };
40550
40455
  }
40551
40456
 
40552
- // src/handlers/feishu-auth.ts
40553
- function buildFeishuAuthHandlers(deps) {
40554
- const loginStart = async () => {
40555
- const { authUrl, state } = deps.loginFlow.start();
40556
- return {
40557
- response: { type: "auth:login:start:ok", authUrl, state }
40558
- };
40559
- };
40560
- const getIdentity = async () => {
40561
- const record = deps.ownerIdentityStore.read();
40562
- return {
40563
- response: {
40564
- type: "auth:getIdentity:ok",
40565
- identity: record ? record.identity : null
40566
- }
40567
- };
40568
- };
40569
- const logout = async () => {
40570
- deps.ownerIdentityStore.clear();
40571
- return {
40572
- response: { type: "auth:logout:ok" }
40573
- };
40574
- };
40575
- return {
40576
- "auth:login:start": loginStart,
40577
- "auth:getIdentity": getIdentity,
40578
- "auth:logout": logout
40579
- };
40580
- }
40581
-
40582
40457
  // src/handlers/meta.ts
40583
40458
  var import_node_os15 = __toESM(require("os"), 1);
40584
40459
  init_protocol();
@@ -40706,7 +40581,7 @@ function buildPersonaHandlers(deps) {
40706
40581
  }
40707
40582
 
40708
40583
  // src/handlers/attachment.ts
40709
- var import_node_path34 = __toESM(require("path"), 1);
40584
+ var import_node_path33 = __toESM(require("path"), 1);
40710
40585
  init_protocol();
40711
40586
  init_protocol();
40712
40587
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -40760,12 +40635,12 @@ function buildAttachmentHandlers(deps) {
40760
40635
  `session ${args.sessionId} scope unresolved`
40761
40636
  );
40762
40637
  }
40763
- const cwdAbs = import_node_path34.default.resolve(sessionFile.cwd);
40764
- const candidateAbs = import_node_path34.default.isAbsolute(args.relPath) ? import_node_path34.default.resolve(args.relPath) : import_node_path34.default.resolve(cwdAbs, args.relPath);
40638
+ const cwdAbs = import_node_path33.default.resolve(sessionFile.cwd);
40639
+ const candidateAbs = import_node_path33.default.isAbsolute(args.relPath) ? import_node_path33.default.resolve(args.relPath) : import_node_path33.default.resolve(cwdAbs, args.relPath);
40765
40640
  assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl");
40766
40641
  const entries = deps.groupFileStore.list(scope, args.sessionId);
40767
40642
  const entry = entries.find((e) => {
40768
- const storedAbs = import_node_path34.default.isAbsolute(e.relPath) ? import_node_path34.default.resolve(e.relPath) : import_node_path34.default.resolve(cwdAbs, e.relPath);
40643
+ const storedAbs = import_node_path33.default.isAbsolute(e.relPath) ? import_node_path33.default.resolve(e.relPath) : import_node_path33.default.resolve(cwdAbs, e.relPath);
40769
40644
  return storedAbs === candidateAbs && !e.stale;
40770
40645
  });
40771
40646
  if (!entry) {
@@ -40789,7 +40664,7 @@ function buildAttachmentHandlers(deps) {
40789
40664
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
40790
40665
  const f = deps.sessionStore.read(sessionId);
40791
40666
  if (!f) return;
40792
- assertGuestAttachmentPath(ctx, import_node_path34.default.resolve(f.cwd), deps.personaRoot, method);
40667
+ assertGuestAttachmentPath(ctx, import_node_path33.default.resolve(f.cwd), deps.personaRoot, method);
40793
40668
  }
40794
40669
  const groupAdd = async (frame, _client, ctx) => {
40795
40670
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -40861,19 +40736,19 @@ function buildAttachmentHandlers(deps) {
40861
40736
 
40862
40737
  // src/handlers/extension.ts
40863
40738
  var import_promises7 = __toESM(require("fs/promises"), 1);
40864
- var import_node_path39 = __toESM(require("path"), 1);
40739
+ var import_node_path38 = __toESM(require("path"), 1);
40865
40740
  init_protocol();
40866
40741
 
40867
40742
  // src/extension/bundle-zip.ts
40868
40743
  var import_promises4 = __toESM(require("fs/promises"), 1);
40869
- var import_node_path35 = __toESM(require("path"), 1);
40870
- var import_node_crypto12 = __toESM(require("crypto"), 1);
40744
+ var import_node_path34 = __toESM(require("path"), 1);
40745
+ var import_node_crypto11 = __toESM(require("crypto"), 1);
40871
40746
  var import_jszip2 = __toESM(require_lib3(), 1);
40872
40747
  async function bundleExtensionDir(dir) {
40873
40748
  const entries = await listFilesSorted(dir);
40874
40749
  const zip = new import_jszip2.default();
40875
40750
  for (const rel of entries) {
40876
- const abs = import_node_path35.default.join(dir, rel);
40751
+ const abs = import_node_path34.default.join(dir, rel);
40877
40752
  const content = await import_promises4.default.readFile(abs);
40878
40753
  zip.file(rel, content, { date: FIXED_DATE });
40879
40754
  }
@@ -40882,7 +40757,7 @@ async function bundleExtensionDir(dir) {
40882
40757
  compression: "DEFLATE",
40883
40758
  compressionOptions: { level: 6 }
40884
40759
  });
40885
- const sha256 = import_node_crypto12.default.createHash("sha256").update(buffer).digest("hex");
40760
+ const sha256 = import_node_crypto11.default.createHash("sha256").update(buffer).digest("hex");
40886
40761
  return { buffer, sha256 };
40887
40762
  }
40888
40763
  var FIXED_DATE = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
@@ -40894,7 +40769,7 @@ async function listFilesSorted(rootDir) {
40894
40769
  return out;
40895
40770
  }
40896
40771
  async function walk(absRoot, relPrefix, out) {
40897
- const dirAbs = import_node_path35.default.join(absRoot, relPrefix);
40772
+ const dirAbs = import_node_path34.default.join(absRoot, relPrefix);
40898
40773
  const entries = await import_promises4.default.readdir(dirAbs, { withFileTypes: true });
40899
40774
  for (const e of entries) {
40900
40775
  if (IGNORE_BASENAMES.has(e.name)) continue;
@@ -40948,25 +40823,25 @@ function computePublishCheck(args) {
40948
40823
 
40949
40824
  // src/extension/install-flow.ts
40950
40825
  var import_promises5 = __toESM(require("fs/promises"), 1);
40951
- var import_node_path37 = __toESM(require("path"), 1);
40826
+ var import_node_path36 = __toESM(require("path"), 1);
40952
40827
  var import_node_os17 = __toESM(require("os"), 1);
40953
- var import_node_crypto13 = __toESM(require("crypto"), 1);
40828
+ var import_node_crypto12 = __toESM(require("crypto"), 1);
40954
40829
  var import_jszip3 = __toESM(require_lib3(), 1);
40955
40830
 
40956
40831
  // src/extension/paths.ts
40957
40832
  var import_node_os16 = __toESM(require("os"), 1);
40958
- var import_node_path36 = __toESM(require("path"), 1);
40833
+ var import_node_path35 = __toESM(require("path"), 1);
40959
40834
  function clawdHomeRoot(override) {
40960
- return override ?? process.env.CLAWD_HOME ?? import_node_path36.default.join(import_node_os16.default.homedir(), ".clawd");
40835
+ return override ?? process.env.CLAWD_HOME ?? import_node_path35.default.join(import_node_os16.default.homedir(), ".clawd");
40961
40836
  }
40962
40837
  function extensionsRoot(override) {
40963
- return import_node_path36.default.join(clawdHomeRoot(override), "extensions");
40838
+ return import_node_path35.default.join(clawdHomeRoot(override), "extensions");
40964
40839
  }
40965
40840
  function publishedChannelsFile(override) {
40966
- return import_node_path36.default.join(clawdHomeRoot(override), "extensions-published.json");
40841
+ return import_node_path35.default.join(clawdHomeRoot(override), "extensions-published.json");
40967
40842
  }
40968
40843
  function bundleCacheRoot(override) {
40969
- return import_node_path36.default.join(clawdHomeRoot(override), "extension-bundles");
40844
+ return import_node_path35.default.join(clawdHomeRoot(override), "extension-bundles");
40970
40845
  }
40971
40846
 
40972
40847
  // src/extension/install-flow.ts
@@ -40979,7 +40854,7 @@ var InstallError = class extends Error {
40979
40854
  };
40980
40855
  async function installFromChannel(args, deps) {
40981
40856
  const { channelRef, snapshotHash, bundleZip } = args;
40982
- const computed = import_node_crypto13.default.createHash("sha256").update(bundleZip).digest("hex");
40857
+ const computed = import_node_crypto12.default.createHash("sha256").update(bundleZip).digest("hex");
40983
40858
  if (computed !== snapshotHash) {
40984
40859
  throw new InstallError(
40985
40860
  "HASH_MISMATCH",
@@ -40993,7 +40868,7 @@ async function installFromChannel(args, deps) {
40993
40868
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
40994
40869
  }
40995
40870
  for (const name of Object.keys(zip.files)) {
40996
- if (name.includes("..") || name.startsWith("/") || import_node_path37.default.isAbsolute(name)) {
40871
+ if (name.includes("..") || name.startsWith("/") || import_node_path36.default.isAbsolute(name)) {
40997
40872
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
40998
40873
  }
40999
40874
  }
@@ -41025,7 +40900,7 @@ async function installFromChannel(args, deps) {
41025
40900
  );
41026
40901
  }
41027
40902
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
41028
- const destDir = import_node_path37.default.join(deps.extensionsRoot, localExtId);
40903
+ const destDir = import_node_path36.default.join(deps.extensionsRoot, localExtId);
41029
40904
  let destExists = false;
41030
40905
  try {
41031
40906
  await import_promises5.default.access(destDir);
@@ -41039,16 +40914,16 @@ async function installFromChannel(args, deps) {
41039
40914
  );
41040
40915
  }
41041
40916
  const stage = await import_promises5.default.mkdtemp(
41042
- import_node_path37.default.join(import_node_os17.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
40917
+ import_node_path36.default.join(import_node_os17.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
41043
40918
  );
41044
40919
  try {
41045
40920
  for (const [name, entry] of Object.entries(zip.files)) {
41046
- const dest = import_node_path37.default.join(stage, name);
40921
+ const dest = import_node_path36.default.join(stage, name);
41047
40922
  if (entry.dir) {
41048
40923
  await import_promises5.default.mkdir(dest, { recursive: true });
41049
40924
  continue;
41050
40925
  }
41051
- await import_promises5.default.mkdir(import_node_path37.default.dirname(dest), { recursive: true });
40926
+ await import_promises5.default.mkdir(import_node_path36.default.dirname(dest), { recursive: true });
41052
40927
  if (name === "manifest.json") {
41053
40928
  const rewritten = { ...parsed.data, id: localExtId };
41054
40929
  await import_promises5.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -41069,9 +40944,9 @@ async function installFromChannel(args, deps) {
41069
40944
 
41070
40945
  // src/extension/update-flow.ts
41071
40946
  var import_promises6 = __toESM(require("fs/promises"), 1);
41072
- var import_node_path38 = __toESM(require("path"), 1);
40947
+ var import_node_path37 = __toESM(require("path"), 1);
41073
40948
  var import_node_os18 = __toESM(require("os"), 1);
41074
- var import_node_crypto14 = __toESM(require("crypto"), 1);
40949
+ var import_node_crypto13 = __toESM(require("crypto"), 1);
41075
40950
  var import_jszip4 = __toESM(require_lib3(), 1);
41076
40951
  var UpdateError = class extends Error {
41077
40952
  constructor(code, message) {
@@ -41086,11 +40961,11 @@ async function updateFromChannel(args, deps) {
41086
40961
  channelRef.extId,
41087
40962
  channelRef.ownerPrincipalId
41088
40963
  );
41089
- const liveDir = import_node_path38.default.join(deps.extensionsRoot, localExtId);
40964
+ const liveDir = import_node_path37.default.join(deps.extensionsRoot, localExtId);
41090
40965
  const prevDir = `${liveDir}.prev`;
41091
40966
  let existingVersion;
41092
40967
  try {
41093
- const raw = await import_promises6.default.readFile(import_node_path38.default.join(liveDir, "manifest.json"), "utf8");
40968
+ const raw = await import_promises6.default.readFile(import_node_path37.default.join(liveDir, "manifest.json"), "utf8");
41094
40969
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
41095
40970
  if (!parsed2.success) {
41096
40971
  throw new UpdateError(
@@ -41109,7 +40984,7 @@ async function updateFromChannel(args, deps) {
41109
40984
  if (e instanceof UpdateError) throw e;
41110
40985
  throw e;
41111
40986
  }
41112
- const computed = import_node_crypto14.default.createHash("sha256").update(bundleZip).digest("hex");
40987
+ const computed = import_node_crypto13.default.createHash("sha256").update(bundleZip).digest("hex");
41113
40988
  if (computed !== snapshotHash) {
41114
40989
  throw new UpdateError(
41115
40990
  "HASH_MISMATCH",
@@ -41123,7 +40998,7 @@ async function updateFromChannel(args, deps) {
41123
40998
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
41124
40999
  }
41125
41000
  for (const name of Object.keys(zip.files)) {
41126
- if (name.includes("..") || name.startsWith("/") || import_node_path38.default.isAbsolute(name)) {
41001
+ if (name.includes("..") || name.startsWith("/") || import_node_path37.default.isAbsolute(name)) {
41127
41002
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
41128
41003
  }
41129
41004
  }
@@ -41158,16 +41033,16 @@ async function updateFromChannel(args, deps) {
41158
41033
  await import_promises6.default.rm(prevDir, { recursive: true, force: true });
41159
41034
  await import_promises6.default.rename(liveDir, prevDir);
41160
41035
  const stage = await import_promises6.default.mkdtemp(
41161
- import_node_path38.default.join(import_node_os18.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
41036
+ import_node_path37.default.join(import_node_os18.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
41162
41037
  );
41163
41038
  try {
41164
41039
  for (const [name, entry] of Object.entries(zip.files)) {
41165
- const dest = import_node_path38.default.join(stage, name);
41040
+ const dest = import_node_path37.default.join(stage, name);
41166
41041
  if (entry.dir) {
41167
41042
  await import_promises6.default.mkdir(dest, { recursive: true });
41168
41043
  continue;
41169
41044
  }
41170
- await import_promises6.default.mkdir(import_node_path38.default.dirname(dest), { recursive: true });
41045
+ await import_promises6.default.mkdir(import_node_path37.default.dirname(dest), { recursive: true });
41171
41046
  if (name === "manifest.json") {
41172
41047
  const rewritten = { ...parsed.data, id: localExtId };
41173
41048
  await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -41260,7 +41135,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
41260
41135
  );
41261
41136
  }
41262
41137
  }
41263
- const manifestPath = import_node_path39.default.join(root, extId, "manifest.json");
41138
+ const manifestPath = import_node_path38.default.join(root, extId, "manifest.json");
41264
41139
  const manifest = await readManifest(root, extId);
41265
41140
  const next = { ...manifest, version: newVersion };
41266
41141
  const tmp = `${manifestPath}.tmp`;
@@ -41268,7 +41143,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
41268
41143
  await import_promises7.default.rename(tmp, manifestPath);
41269
41144
  }
41270
41145
  async function readManifest(root, extId) {
41271
- const file = import_node_path39.default.join(root, extId, "manifest.json");
41146
+ const file = import_node_path38.default.join(root, extId, "manifest.json");
41272
41147
  let raw;
41273
41148
  try {
41274
41149
  raw = await import_promises7.default.readFile(file, "utf8");
@@ -41359,7 +41234,7 @@ function buildExtensionHandlers(deps) {
41359
41234
  };
41360
41235
  async function buildSnapshotMeta(extId) {
41361
41236
  const manifest = await readManifest(deps.root, extId);
41362
- const { sha256, buffer } = await bundleExtensionDir(import_node_path39.default.join(deps.root, extId));
41237
+ const { sha256, buffer } = await bundleExtensionDir(import_node_path38.default.join(deps.root, extId));
41363
41238
  return { manifest, contentHash: sha256, buffer };
41364
41239
  }
41365
41240
  const publish = async (frame, _client, ctx) => {
@@ -41539,9 +41414,64 @@ function buildExtensionHandlers(deps) {
41539
41414
  };
41540
41415
  }
41541
41416
 
41417
+ // src/handlers/app-builder.ts
41418
+ function buildAppBuilderHandlers(deps) {
41419
+ const { store, deleteSessionsByProject, devServerLifecycle } = deps;
41420
+ const listProjects = async () => {
41421
+ const projects = await store.list();
41422
+ return {
41423
+ response: {
41424
+ projects: projects.map((p2) => ({
41425
+ ...p2,
41426
+ isRunning: devServerLifecycle.isRunning(p2.name),
41427
+ boundSessionId: devServerLifecycle.boundSessionId(p2.name)
41428
+ }))
41429
+ }
41430
+ };
41431
+ };
41432
+ const createProject = async (frame) => {
41433
+ const name = frame.name;
41434
+ if (typeof name !== "string") throw new Error("createProject: name must be string");
41435
+ const project = await store.create(name);
41436
+ return { response: { project } };
41437
+ };
41438
+ const deleteProject = async (frame) => {
41439
+ const name = frame.name;
41440
+ if (typeof name !== "string") throw new Error("deleteProject: name must be string");
41441
+ await devServerLifecycle.stopForProject(name);
41442
+ await deleteSessionsByProject(name);
41443
+ await store.delete(name);
41444
+ return { response: {} };
41445
+ };
41446
+ const updateProjectPort = async (frame) => {
41447
+ const f = frame;
41448
+ if (typeof f.name !== "string") throw new Error("updateProjectPort: name must be string");
41449
+ if (typeof f.newPort !== "number") throw new Error("updateProjectPort: newPort must be number");
41450
+ const entry = devServerLifecycle.boundEntry(f.name);
41451
+ await devServerLifecycle.stopForProject(f.name);
41452
+ const project = await store.updatePort(f.name, f.newPort);
41453
+ if (entry) {
41454
+ await devServerLifecycle.restartForProjectAt({
41455
+ projectName: f.name,
41456
+ newPort: f.newPort,
41457
+ sessionId: entry.sessionId,
41458
+ cwd: entry.cwd,
41459
+ tunnelHost: entry.tunnelHost
41460
+ });
41461
+ }
41462
+ return { response: { project } };
41463
+ };
41464
+ return {
41465
+ "appBuilder:listProjects": listProjects,
41466
+ "appBuilder:createProject": createProject,
41467
+ "appBuilder:deleteProject": deleteProject,
41468
+ "appBuilder:updateProjectPort": updateProjectPort
41469
+ };
41470
+ }
41471
+
41542
41472
  // src/extension/registry.ts
41543
41473
  var import_promises8 = __toESM(require("fs/promises"), 1);
41544
- var import_node_path40 = __toESM(require("path"), 1);
41474
+ var import_node_path39 = __toESM(require("path"), 1);
41545
41475
  async function loadAll(root) {
41546
41476
  let entries;
41547
41477
  try {
@@ -41554,13 +41484,13 @@ async function loadAll(root) {
41554
41484
  for (const ent of entries) {
41555
41485
  if (!ent.isDirectory()) continue;
41556
41486
  if (ent.name.startsWith(".")) continue;
41557
- records.push(await loadOne(import_node_path40.default.join(root, ent.name), ent.name));
41487
+ records.push(await loadOne(import_node_path39.default.join(root, ent.name), ent.name));
41558
41488
  }
41559
41489
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
41560
41490
  return records;
41561
41491
  }
41562
41492
  async function loadOne(dir, dirName) {
41563
- const manifestPath = import_node_path40.default.join(dir, "manifest.json");
41493
+ const manifestPath = import_node_path39.default.join(dir, "manifest.json");
41564
41494
  let raw;
41565
41495
  try {
41566
41496
  raw = await import_promises8.default.readFile(manifestPath, "utf8");
@@ -41605,7 +41535,7 @@ async function loadOne(dir, dirName) {
41605
41535
 
41606
41536
  // src/extension/uninstall.ts
41607
41537
  var import_promises9 = __toESM(require("fs/promises"), 1);
41608
- var import_node_path41 = __toESM(require("path"), 1);
41538
+ var import_node_path40 = __toESM(require("path"), 1);
41609
41539
  var UninstallError = class extends Error {
41610
41540
  constructor(code, message) {
41611
41541
  super(message);
@@ -41614,7 +41544,7 @@ var UninstallError = class extends Error {
41614
41544
  code;
41615
41545
  };
41616
41546
  async function uninstall(deps) {
41617
- const dir = import_node_path41.default.join(deps.root, deps.extId);
41547
+ const dir = import_node_path40.default.join(deps.root, deps.extId);
41618
41548
  try {
41619
41549
  await import_promises9.default.access(dir);
41620
41550
  } catch {
@@ -41627,7 +41557,12 @@ async function uninstall(deps) {
41627
41557
  // src/handlers/index.ts
41628
41558
  function buildMethodHandlers(deps) {
41629
41559
  return {
41630
- ...buildSessionHandlers(deps),
41560
+ ...buildSessionHandlers({
41561
+ ...deps,
41562
+ devServerLifecycle: deps.devServerLifecycle,
41563
+ appBuilderStore: deps.appBuilderStore,
41564
+ getTunnelUrl: deps.getTunnelUrl
41565
+ }),
41631
41566
  ...buildPermissionHandlers(deps),
41632
41567
  ...buildHistoryHandlers(deps),
41633
41568
  ...buildWorkspaceHandlers(deps),
@@ -41660,7 +41595,6 @@ function buildMethodHandlers(deps) {
41660
41595
  personaStore: deps.personaStore,
41661
41596
  capabilityManager: deps.capabilityManager
41662
41597
  }),
41663
- ...buildFeishuAuthHandlers(deps.feishuAuth),
41664
41598
  ...deps.attachment ? buildAttachmentHandlers(deps.attachment) : {},
41665
41599
  ...buildExtensionHandlers({
41666
41600
  loadAll,
@@ -41669,10 +41603,186 @@ function buildMethodHandlers(deps) {
41669
41603
  root: extensionsRoot(),
41670
41604
  publishedChannelStore: deps.publishedChannelStore,
41671
41605
  bundleCache: deps.bundleCache
41606
+ }),
41607
+ ...buildAppBuilderHandlers({
41608
+ store: deps.appBuilderStore,
41609
+ deleteSessionsByProject: deps.deleteSessionsByProject,
41610
+ devServerLifecycle: deps.devServerLifecycle
41672
41611
  })
41673
41612
  };
41674
41613
  }
41675
41614
 
41615
+ // src/app-builder/project-store.ts
41616
+ var import_node_fs29 = require("fs");
41617
+ var import_node_path41 = require("path");
41618
+ init_protocol();
41619
+ var PROJECTS_DIR = "projects";
41620
+ var META_FILE = ".clawd-project.json";
41621
+ var ProjectStore = class {
41622
+ /** @param personaRoot persona 目录,例如 `~/.clawd/personas/persona-app-builder/` */
41623
+ constructor(personaRoot) {
41624
+ this.personaRoot = personaRoot;
41625
+ }
41626
+ personaRoot;
41627
+ /** projects/<name>/.clawd-project.json 路径 */
41628
+ metaPath(name) {
41629
+ return (0, import_node_path41.join)(this.projectsRoot(), name, META_FILE);
41630
+ }
41631
+ /** projects/<name>/ 目录路径(cwd 用) */
41632
+ projectDir(name) {
41633
+ return (0, import_node_path41.join)(this.projectsRoot(), name);
41634
+ }
41635
+ projectsRoot() {
41636
+ return (0, import_node_path41.join)(this.personaRoot, PROJECTS_DIR);
41637
+ }
41638
+ async list() {
41639
+ let entries;
41640
+ try {
41641
+ entries = await import_node_fs29.promises.readdir(this.projectsRoot());
41642
+ } catch (err) {
41643
+ if (err.code === "ENOENT") return [];
41644
+ throw err;
41645
+ }
41646
+ const out = [];
41647
+ for (const name of entries) {
41648
+ try {
41649
+ const raw = await import_node_fs29.promises.readFile(this.metaPath(name), "utf8");
41650
+ const parsed = ProjectMetadataSchema.safeParse(JSON.parse(raw));
41651
+ if (parsed.success) out.push(parsed.data);
41652
+ } catch {
41653
+ }
41654
+ }
41655
+ return out.sort((a, b2) => a.name.localeCompare(b2.name));
41656
+ }
41657
+ async create(name) {
41658
+ const existing = await this.list();
41659
+ if (existing.some((p2) => p2.name === name)) {
41660
+ throw new Error(`project "${name}" already exists / \u5DF2\u5B58\u5728`);
41661
+ }
41662
+ const usedPorts = new Set(existing.map((p2) => p2.port));
41663
+ let port = -1;
41664
+ for (let p2 = PROJECT_PORT_MIN; p2 <= PROJECT_PORT_MAX; p2++) {
41665
+ if (!usedPorts.has(p2)) {
41666
+ port = p2;
41667
+ break;
41668
+ }
41669
+ }
41670
+ if (port < 0) {
41671
+ throw new Error(
41672
+ `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`
41673
+ );
41674
+ }
41675
+ const meta = {
41676
+ name,
41677
+ port,
41678
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
41679
+ };
41680
+ const validated = ProjectMetadataSchema.safeParse(meta);
41681
+ if (!validated.success) {
41682
+ throw new Error(`invalid name "${name}": ${validated.error.message}`);
41683
+ }
41684
+ const dir = this.projectDir(name);
41685
+ await import_node_fs29.promises.mkdir(dir, { recursive: true });
41686
+ await import_node_fs29.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
41687
+ return meta;
41688
+ }
41689
+ async delete(name) {
41690
+ const dir = this.projectDir(name);
41691
+ await import_node_fs29.promises.rm(dir, { recursive: true, force: true });
41692
+ }
41693
+ async updatePort(name, newPort) {
41694
+ if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
41695
+ throw new Error(
41696
+ `port range invalid: must be ${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}, got ${newPort}`
41697
+ );
41698
+ }
41699
+ const list = await this.list();
41700
+ const target = list.find((p2) => p2.name === name);
41701
+ if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
41702
+ const conflict = list.find((p2) => p2.name !== name && p2.port === newPort);
41703
+ if (conflict) {
41704
+ throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
41705
+ }
41706
+ const updated = { ...target, port: newPort };
41707
+ await import_node_fs29.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
41708
+ return updated;
41709
+ }
41710
+ };
41711
+
41712
+ // src/app-builder/dev-server-supervisor.ts
41713
+ var import_node_child_process7 = require("child_process");
41714
+ var import_node_events2 = require("events");
41715
+ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
41716
+ running = /* @__PURE__ */ new Map();
41717
+ // key: sessionId
41718
+ startForSession(args) {
41719
+ if (this.running.has(args.sessionId)) return;
41720
+ const env = {
41721
+ ...process.env,
41722
+ CLAWD_TUNNEL_HOST: args.tunnelHost ?? "",
41723
+ CLAWD_PREVIEW_PORT: String(args.port)
41724
+ };
41725
+ const child = (0, import_node_child_process7.spawn)("pnpm", ["dev"], { cwd: args.cwd, env, stdio: "pipe" });
41726
+ const entry = { ...args, process: child };
41727
+ this.running.set(args.sessionId, entry);
41728
+ child.on("exit", (code) => {
41729
+ this.running.delete(args.sessionId);
41730
+ if (code !== 0 && code !== null) {
41731
+ const ev = {
41732
+ sessionId: args.sessionId,
41733
+ projectName: args.projectName,
41734
+ port: args.port,
41735
+ exitCode: code
41736
+ };
41737
+ this.emit("devServer:failed", ev);
41738
+ }
41739
+ });
41740
+ }
41741
+ stopForSession(sessionId) {
41742
+ const entry = this.running.get(sessionId);
41743
+ if (!entry) return Promise.resolve();
41744
+ return new Promise((resolve6) => {
41745
+ entry.process.once("exit", () => {
41746
+ this.running.delete(sessionId);
41747
+ resolve6();
41748
+ });
41749
+ entry.process.kill("SIGTERM");
41750
+ });
41751
+ }
41752
+ async stopForProject(projectName) {
41753
+ const entry = [...this.running.values()].find((e) => e.projectName === projectName);
41754
+ if (!entry) return;
41755
+ await this.stopForSession(entry.sessionId);
41756
+ }
41757
+ async restartForProjectAt(args) {
41758
+ await this.stopForSession(args.sessionId);
41759
+ this.startForSession({
41760
+ sessionId: args.sessionId,
41761
+ projectName: args.projectName,
41762
+ port: args.newPort,
41763
+ cwd: args.cwd,
41764
+ tunnelHost: args.tunnelHost
41765
+ });
41766
+ }
41767
+ isRunning(projectName) {
41768
+ return [...this.running.values()].some((e) => e.projectName === projectName);
41769
+ }
41770
+ boundSessionId(projectName) {
41771
+ const entry = [...this.running.values()].find((e) => e.projectName === projectName);
41772
+ return entry?.sessionId ?? null;
41773
+ }
41774
+ boundEntry(projectName) {
41775
+ const entry = [...this.running.values()].find((e) => e.projectName === projectName);
41776
+ if (!entry) return null;
41777
+ return {
41778
+ sessionId: entry.sessionId,
41779
+ cwd: entry.cwd,
41780
+ port: entry.port,
41781
+ tunnelHost: entry.tunnelHost
41782
+ };
41783
+ }
41784
+ };
41785
+
41676
41786
  // src/handlers/method-grants.ts
41677
41787
  var ADMIN_ANY = {
41678
41788
  kind: "fixed",
@@ -41803,11 +41913,14 @@ var METHOD_GRANT_MAP = {
41803
41913
  // guest-self:guest 在自己 daemon 上触发安装与更新(无持久化 subscription 概念)
41804
41914
  "extension.install-from-channel": ADMIN_ANY,
41805
41915
  "extension.update-from-channel": ADMIN_ANY,
41806
- // ---- auth:* 飞书统一身份 Phase 1(owner-only) ----
41807
- // 登录/登出/查身份都是 owner 本机操作,guest 不可调
41808
- "auth:login:start": ADMIN_ANY,
41809
- "auth:getIdentity": ADMIN_ANY,
41810
- "auth:logout": ADMIN_ANY
41916
+ // ---- app-builder Project picker(spec 2026-06-01-app-builder-project-picker-design) ----
41917
+ // owner-only:picker UI 给 owner 管理本机 build 中的 project(fs + 端口分配)。
41918
+ // 即使 persona-app-builder 是 PreinstalledPersona,project 管理也属本机 owner 行为,
41919
+ // 不暴露给 guest(远端接入者)。
41920
+ "appBuilder:listProjects": ADMIN_ANY,
41921
+ "appBuilder:createProject": ADMIN_ANY,
41922
+ "appBuilder:deleteProject": ADMIN_ANY,
41923
+ "appBuilder:updateProjectPort": ADMIN_ANY
41811
41924
  };
41812
41925
  function computeGrantForFrame(method, frame) {
41813
41926
  const rule = METHOD_GRANT_MAP[method];
@@ -41823,12 +41936,12 @@ function computeGrantForFrame(method, frame) {
41823
41936
  }
41824
41937
 
41825
41938
  // src/extension/runtime.ts
41826
- var import_node_child_process7 = require("child_process");
41939
+ var import_node_child_process8 = require("child_process");
41827
41940
  var import_node_path42 = __toESM(require("path"), 1);
41828
41941
  var import_promises10 = require("timers/promises");
41829
41942
 
41830
41943
  // src/extension/port-allocator.ts
41831
- var import_node_net = __toESM(require("net"), 1);
41944
+ var import_node_net2 = __toESM(require("net"), 1);
41832
41945
  var PortExhaustedError = class extends Error {
41833
41946
  constructor(min, max) {
41834
41947
  super(`no free port in [${min},${max}]`);
@@ -41841,7 +41954,7 @@ var PortExhaustedError = class extends Error {
41841
41954
  };
41842
41955
  function probe(port) {
41843
41956
  return new Promise((resolve6) => {
41844
- const srv = import_node_net.default.createServer();
41957
+ const srv = import_node_net2.default.createServer();
41845
41958
  srv.once("error", () => resolve6(false));
41846
41959
  srv.once("listening", () => {
41847
41960
  srv.close(() => resolve6(true));
@@ -41931,7 +42044,7 @@ var Runtime = class {
41931
42044
  CLAWOS_EXT_PORT: String(port),
41932
42045
  CLAWOS_EXT_ID: extId
41933
42046
  };
41934
- const child = (0, import_node_child_process7.spawn)("sh", ["-c", cmd], {
42047
+ const child = (0, import_node_child_process8.spawn)("sh", ["-c", cmd], {
41935
42048
  cwd: dir,
41936
42049
  env,
41937
42050
  stdio: ["ignore", "pipe", "pipe"],
@@ -42197,11 +42310,8 @@ async function startDaemon(config) {
42197
42310
  } else if (config.tunnel) {
42198
42311
  resolvedAuthToken = authFile.token;
42199
42312
  }
42200
- const ownerIdentityStore = new OwnerIdentityStore(config.dataDir);
42201
- const feishuIdentity = ownerIdentityStore.read();
42202
- let feishuActive = feishuIdentity !== null;
42203
- let ownerPrincipalId = feishuIdentity?.identity.unionId ?? authFile.ownerPrincipalId;
42204
- let ownerDisplayName = feishuIdentity?.identity.displayName ?? loadOwnerDisplayName(config.dataDir);
42313
+ const ownerPrincipalId = authFile.ownerPrincipalId;
42314
+ const ownerDisplayName = loadOwnerDisplayName(config.dataDir);
42205
42315
  const authMode = resolvedAuthToken == null ? "none" : "first-message";
42206
42316
  const inboxStore = new InboxStore(config.dataDir);
42207
42317
  const inboxManager = new InboxManager(inboxStore, (_peerOwnerId, frame) => {
@@ -42243,9 +42353,7 @@ async function startDaemon(config) {
42243
42353
  isOwnerToken: (x) => resolvedAuthToken != null && constantTimeEqual(x, resolvedAuthToken),
42244
42354
  ownerPrincipalId,
42245
42355
  ownerDisplayName,
42246
- // 飞书 gate(spec 决策 #9):未激活飞书身份时不接受 guest(capability token)入站——
42247
- // 不传 registry 让 guest token 一律 BAD_TOKEN。owner 自己连接不受影响。
42248
- ...feishuActive ? { capabilityRegistry } : {},
42356
+ capabilityRegistry,
42249
42357
  selfPrincipalId,
42250
42358
  selfDisplayName
42251
42359
  });
@@ -42402,13 +42510,13 @@ async function startDaemon(config) {
42402
42510
  const publishedChannelStore = new PublishedChannelStore(publishedChannelsFile());
42403
42511
  await publishedChannelStore.load();
42404
42512
  const bundleCache = new BundleCache(bundleCacheRoot());
42405
- const loginFlow = new LoginFlow({
42406
- store: ownerIdentityStore,
42407
- fetchUserInfo: (ttcToken) => fetchTtcUserInfo({ apiBase: TTC_HOSTS.api, token: ttcToken }),
42408
- ttcAuthBase: TTC_HOSTS.auth,
42409
- getCallbackUrl: () => `http://127.0.0.1:${config.port}/auth/callback`
42410
- });
42411
- const makeHandlers = () => buildMethodHandlers({
42513
+ const appBuilderStore = new ProjectStore(
42514
+ import_node_path45.default.join(config.dataDir, "personas/persona-app-builder")
42515
+ );
42516
+ const devServerSupervisor = new DevServerSupervisor();
42517
+ const deleteSessionsByProject = async (_name) => {
42518
+ };
42519
+ const handlers = buildMethodHandlers({
42412
42520
  manager,
42413
42521
  workspace,
42414
42522
  skills,
@@ -42481,10 +42589,11 @@ async function startDaemon(config) {
42481
42589
  // extension sharing v1 — owner-side published-channels store.
42482
42590
  publishedChannelStore,
42483
42591
  bundleCache,
42484
- // 飞书统一身份 Phase 1:登录 RPC handlers(auth:login:start / getIdentity / logout)
42485
- feishuAuth: { loginFlow, ownerIdentityStore }
42592
+ // app-builder
42593
+ appBuilderStore,
42594
+ deleteSessionsByProject,
42595
+ devServerLifecycle: devServerSupervisor
42486
42596
  });
42487
- let handlers = makeHandlers();
42488
42597
  const authResolver = new AuthContextResolver({
42489
42598
  ownerToken: resolvedAuthToken
42490
42599
  });
@@ -42510,29 +42619,8 @@ async function startDaemon(config) {
42510
42619
  },
42511
42620
  // POST /extensions/import lands in ~/.clawd/extensions/<id>/
42512
42621
  extensionsRoot: () => extensionsRoot(),
42513
- // 飞书登录 loopback 回调(热切换,2026-06-01 拍板):成功落盘后——
42514
- // 1. 进程内实时切换身份(let binding + 值快照持有者重建)
42515
- // 2. 广播 auth:login:done 给在线 owner 连接(UI 即时反馈)
42516
- // 3. 踢掉所有 ws 连接(在线连接的 ctx 是旧身份)→ UI 自动重连 → 新身份 ctx →
42517
- // People 立即解锁,不需要重启 daemon
42518
- // 失败则广播 auth:login:failed。
42519
- feishuLogin: {
42520
- handleLoginCallback: async (params) => {
42521
- const result = await loginFlow.handleCallback(params);
42522
- if (result.ok) {
42523
- feishuActive = true;
42524
- ownerPrincipalId = result.identity.unionId;
42525
- ownerDisplayName = result.identity.displayName;
42526
- handlers = makeHandlers();
42527
- wsServer?.setOwnerPrincipalId(result.identity.unionId);
42528
- wsServer?.broadcastToOwners({ type: "auth:login:done", identity: result.identity });
42529
- setImmediate(() => wsServer?.closeAllClients());
42530
- } else {
42531
- wsServer?.broadcastToOwners({ type: "auth:login:failed", reason: result.reason });
42532
- }
42533
- return result;
42534
- }
42535
- }
42622
+ // app-builder build 模式:/preview/<port>/ 反代 dev server 的端口白名单(见 spec §三.2)
42623
+ previewPorts: config.previewPorts ?? void 0
42536
42624
  });
42537
42625
  wsServer = new LocalWsServer({
42538
42626
  host: config.host,
@@ -42571,6 +42659,8 @@ async function startDaemon(config) {
42571
42659
  },
42572
42660
  // file-sharing HTTP 路由复用 daemon 同端口(spec §5 第 3 条);router 自己处理 auth + 404
42573
42661
  httpRequestHandler: httpRouter,
42662
+ // app-builder build 模式:/preview/<port>/ HMR websocket upgrade 转发的端口白名单(同 http-router 配置)
42663
+ previewPorts: config.previewPorts ?? void 0,
42574
42664
  // 订阅成功后给该 client 重放 in-flight pendingQuestions(plan: clawd-question-server-truth)。
42575
42665
  // daemon 是 pendingQuestions 的唯一 source of truth;新 client 接入 / 刷新页面时
42576
42666
  // 把当前所有未决 question 以 session:question 帧定向回放,让 UI 不再误显示 Ended。
@@ -42615,12 +42705,6 @@ async function startDaemon(config) {
42615
42705
  const requestId = typeof frame.requestId === "string" ? frame.requestId : void 0;
42616
42706
  const handler = handlers[type];
42617
42707
  if (!handler) throw new ClawdError(ERROR_CODES.METHOD_NOT_IMPLEMENTED, `not implemented: ${type}`);
42618
- if (!feishuActive && FEISHU_GATED_METHODS.includes(type)) {
42619
- throw new ClawdError(
42620
- ERROR_CODES.FEISHU_LOGIN_REQUIRED,
42621
- `${type} requires feishu login (People/remote identity)`
42622
- );
42623
- }
42624
42708
  const ctx = wsServer.getClientContext(client.id) ?? ownerContext(ownerPrincipalId, ownerDisplayName);
42625
42709
  const verdict = computeGrantForFrame(type, frame);
42626
42710
  if (verdict.kind === "check") {
@@ -42693,6 +42777,7 @@ async function startDaemon(config) {
42693
42777
  stateSnapshot = { ...stateSnapshot, tunnelUrl: r.url, tunnelError: void 0 };
42694
42778
  stateMgr.write(stateSnapshot);
42695
42779
  currentTunnelUrl = r.url;
42780
+ wss.broadcastAll({ type: "tunnel:ready", url: r.url, subdomain: r.subdomain });
42696
42781
  const connectUrl = resolvedAuthToken ? `${r.url}#token=${resolvedAuthToken}` : r.url;
42697
42782
  const lines = [
42698
42783
  `Tunnel: ${r.url}`,