@clawos-dev/clawd 0.2.108-beta.205.1e8d4c5 → 0.2.108-beta.206.f2600cc

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 +595 -546
  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
  });
@@ -5112,12 +5121,6 @@ var init_schemas = __esm({
5112
5121
  * 后调 capabilityManager.recordPeerHello(capId, ownerPrincipalId, displayName)
5113
5122
  * 把 cap.peerOwnerId / firstUsedByPeerAt 字段回写,让 owner 端 UI 在 People
5114
5123
  * 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
5124
  */
5122
5125
  selfPrincipalId: external_exports.string().min(1).optional(),
5123
5126
  /**
@@ -5132,6 +5135,11 @@ var init_schemas = __esm({
5132
5135
  version: external_exports.string().min(1).optional(),
5133
5136
  protocolVersion: external_exports.number().int().nonnegative().optional()
5134
5137
  });
5138
+ TunnelReadyEventSchema = external_exports.object({
5139
+ type: external_exports.literal("tunnel:ready"),
5140
+ url: external_exports.string().min(1),
5141
+ subdomain: external_exports.string().min(1)
5142
+ });
5135
5143
  TunnelExitedEventSchema = external_exports.object({
5136
5144
  type: external_exports.literal("tunnel:exited"),
5137
5145
  code: external_exports.number().int().nullable(),
@@ -5173,6 +5181,41 @@ var init_schemas = __esm({
5173
5181
  /** file-sharing HTTP 路由的 Authorization: Bearer token;与 WS token 解耦,HTTP 401 仅触发 ReAuth 不强踢 WS(spec §11 第 4 条) */
5174
5182
  httpToken: external_exports.string().optional()
5175
5183
  });
5184
+ PROJECT_PORT_MIN = 6173;
5185
+ PROJECT_PORT_MAX = 6182;
5186
+ projectNameRegex = /^[a-z][a-z0-9-]{0,39}$/;
5187
+ ProjectMetadataSchema = external_exports.object({
5188
+ name: external_exports.string().regex(projectNameRegex, {
5189
+ 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"
5190
+ }),
5191
+ port: external_exports.number().int().min(PROJECT_PORT_MIN).max(PROJECT_PORT_MAX),
5192
+ createdAt: external_exports.string().datetime()
5193
+ });
5194
+ ProjectWithStatusSchema = ProjectMetadataSchema.extend({
5195
+ isRunning: external_exports.boolean(),
5196
+ boundSessionId: external_exports.string().nullable()
5197
+ });
5198
+ ListProjectsArgsSchema = external_exports.object({}).strict();
5199
+ ListProjectsResultSchema = external_exports.object({
5200
+ projects: external_exports.array(ProjectWithStatusSchema)
5201
+ }).strict();
5202
+ CreateProjectArgsSchema = external_exports.object({
5203
+ name: external_exports.string()
5204
+ }).strict();
5205
+ CreateProjectResultSchema = external_exports.object({
5206
+ project: ProjectMetadataSchema
5207
+ }).strict();
5208
+ DeleteProjectArgsSchema = external_exports.object({
5209
+ name: external_exports.string()
5210
+ }).strict();
5211
+ DeleteProjectResultSchema = external_exports.object({}).strict();
5212
+ UpdateProjectPortArgsSchema = external_exports.object({
5213
+ name: external_exports.string(),
5214
+ newPort: external_exports.number().int()
5215
+ }).strict();
5216
+ UpdateProjectPortResultSchema = external_exports.object({
5217
+ project: ProjectMetadataSchema
5218
+ }).strict();
5176
5219
  }
5177
5220
  });
5178
5221
 
@@ -5581,64 +5624,6 @@ var init_extension = __esm({
5581
5624
  }
5582
5625
  });
5583
5626
 
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
5627
  // ../protocol/src/runtime.ts
5643
5628
  var init_runtime = __esm({
5644
5629
  "../protocol/src/runtime.ts"() {
@@ -5656,7 +5641,6 @@ var init_runtime = __esm({
5656
5641
  init_inbox();
5657
5642
  init_received_capability();
5658
5643
  init_extension();
5659
- init_feishu_auth();
5660
5644
  }
5661
5645
  });
5662
5646
 
@@ -5931,8 +5915,8 @@ var require_req = __commonJS({
5931
5915
  if (req.originalUrl) {
5932
5916
  _req.url = req.originalUrl;
5933
5917
  } else {
5934
- const path60 = req.path;
5935
- _req.url = typeof path60 === "string" ? path60 : req.url ? req.url.path || req.url : void 0;
5918
+ const path59 = req.path;
5919
+ _req.url = typeof path59 === "string" ? path59 : req.url ? req.url.path || req.url : void 0;
5936
5920
  }
5937
5921
  if (req.query) {
5938
5922
  _req.query = req.query;
@@ -6097,14 +6081,14 @@ var require_redact = __commonJS({
6097
6081
  }
6098
6082
  return obj;
6099
6083
  }
6100
- function parsePath(path60) {
6084
+ function parsePath(path59) {
6101
6085
  const parts = [];
6102
6086
  let current = "";
6103
6087
  let inBrackets = false;
6104
6088
  let inQuotes = false;
6105
6089
  let quoteChar = "";
6106
- for (let i = 0; i < path60.length; i++) {
6107
- const char = path60[i];
6090
+ for (let i = 0; i < path59.length; i++) {
6091
+ const char = path59[i];
6108
6092
  if (!inBrackets && char === ".") {
6109
6093
  if (current) {
6110
6094
  parts.push(current);
@@ -6235,10 +6219,10 @@ var require_redact = __commonJS({
6235
6219
  return current;
6236
6220
  }
6237
6221
  function redactPaths(obj, paths, censor, remove = false) {
6238
- for (const path60 of paths) {
6239
- const parts = parsePath(path60);
6222
+ for (const path59 of paths) {
6223
+ const parts = parsePath(path59);
6240
6224
  if (parts.includes("*")) {
6241
- redactWildcardPath(obj, parts, censor, path60, remove);
6225
+ redactWildcardPath(obj, parts, censor, path59, remove);
6242
6226
  } else {
6243
6227
  if (remove) {
6244
6228
  removeKey(obj, parts);
@@ -6323,8 +6307,8 @@ var require_redact = __commonJS({
6323
6307
  }
6324
6308
  } else {
6325
6309
  if (afterWildcard.includes("*")) {
6326
- const wrappedCensor = typeof censor === "function" ? (value, path60) => {
6327
- const fullPath = [...pathArray.slice(0, pathLength), ...path60];
6310
+ const wrappedCensor = typeof censor === "function" ? (value, path59) => {
6311
+ const fullPath = [...pathArray.slice(0, pathLength), ...path59];
6328
6312
  return censor(value, fullPath);
6329
6313
  } : censor;
6330
6314
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -6359,8 +6343,8 @@ var require_redact = __commonJS({
6359
6343
  return null;
6360
6344
  }
6361
6345
  const pathStructure = /* @__PURE__ */ new Map();
6362
- for (const path60 of pathsToClone) {
6363
- const parts = parsePath(path60);
6346
+ for (const path59 of pathsToClone) {
6347
+ const parts = parsePath(path59);
6364
6348
  let current = pathStructure;
6365
6349
  for (let i = 0; i < parts.length; i++) {
6366
6350
  const part = parts[i];
@@ -6412,24 +6396,24 @@ var require_redact = __commonJS({
6412
6396
  }
6413
6397
  return cloneSelectively(obj, pathStructure);
6414
6398
  }
6415
- function validatePath(path60) {
6416
- if (typeof path60 !== "string") {
6399
+ function validatePath(path59) {
6400
+ if (typeof path59 !== "string") {
6417
6401
  throw new Error("Paths must be (non-empty) strings");
6418
6402
  }
6419
- if (path60 === "") {
6403
+ if (path59 === "") {
6420
6404
  throw new Error("Invalid redaction path ()");
6421
6405
  }
6422
- if (path60.includes("..")) {
6423
- throw new Error(`Invalid redaction path (${path60})`);
6406
+ if (path59.includes("..")) {
6407
+ throw new Error(`Invalid redaction path (${path59})`);
6424
6408
  }
6425
- if (path60.includes(",")) {
6426
- throw new Error(`Invalid redaction path (${path60})`);
6409
+ if (path59.includes(",")) {
6410
+ throw new Error(`Invalid redaction path (${path59})`);
6427
6411
  }
6428
6412
  let bracketCount = 0;
6429
6413
  let inQuotes = false;
6430
6414
  let quoteChar = "";
6431
- for (let i = 0; i < path60.length; i++) {
6432
- const char = path60[i];
6415
+ for (let i = 0; i < path59.length; i++) {
6416
+ const char = path59[i];
6433
6417
  if ((char === '"' || char === "'") && bracketCount > 0) {
6434
6418
  if (!inQuotes) {
6435
6419
  inQuotes = true;
@@ -6443,20 +6427,20 @@ var require_redact = __commonJS({
6443
6427
  } else if (char === "]" && !inQuotes) {
6444
6428
  bracketCount--;
6445
6429
  if (bracketCount < 0) {
6446
- throw new Error(`Invalid redaction path (${path60})`);
6430
+ throw new Error(`Invalid redaction path (${path59})`);
6447
6431
  }
6448
6432
  }
6449
6433
  }
6450
6434
  if (bracketCount !== 0) {
6451
- throw new Error(`Invalid redaction path (${path60})`);
6435
+ throw new Error(`Invalid redaction path (${path59})`);
6452
6436
  }
6453
6437
  }
6454
6438
  function validatePaths(paths) {
6455
6439
  if (!Array.isArray(paths)) {
6456
6440
  throw new TypeError("paths must be an array");
6457
6441
  }
6458
- for (const path60 of paths) {
6459
- validatePath(path60);
6442
+ for (const path59 of paths) {
6443
+ validatePath(path59);
6460
6444
  }
6461
6445
  }
6462
6446
  function slowRedact(options = {}) {
@@ -6624,8 +6608,8 @@ var require_redaction = __commonJS({
6624
6608
  if (shape[k2] === null) {
6625
6609
  o[k2] = (value) => topCensor(value, [k2]);
6626
6610
  } else {
6627
- const wrappedCensor = typeof censor === "function" ? (value, path60) => {
6628
- return censor(value, [k2, ...path60]);
6611
+ const wrappedCensor = typeof censor === "function" ? (value, path59) => {
6612
+ return censor(value, [k2, ...path59]);
6629
6613
  } : censor;
6630
6614
  o[k2] = Redact({
6631
6615
  paths: shape[k2],
@@ -6844,9 +6828,9 @@ var require_sonic_boom = __commonJS({
6844
6828
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
6845
6829
  "use strict";
6846
6830
  var fs48 = require("fs");
6847
- var EventEmitter2 = require("events");
6831
+ var EventEmitter3 = require("events");
6848
6832
  var inherits = require("util").inherits;
6849
- var path60 = require("path");
6833
+ var path59 = require("path");
6850
6834
  var sleep = require_atomic_sleep();
6851
6835
  var assert = require("assert");
6852
6836
  var BUSY_WRITE_TIMEOUT = 100;
@@ -6900,7 +6884,7 @@ var require_sonic_boom = __commonJS({
6900
6884
  const mode = sonic.mode;
6901
6885
  if (sonic.sync) {
6902
6886
  try {
6903
- if (sonic.mkdir) fs48.mkdirSync(path60.dirname(file), { recursive: true });
6887
+ if (sonic.mkdir) fs48.mkdirSync(path59.dirname(file), { recursive: true });
6904
6888
  const fd = fs48.openSync(file, flags, mode);
6905
6889
  fileOpened(null, fd);
6906
6890
  } catch (err) {
@@ -6908,7 +6892,7 @@ var require_sonic_boom = __commonJS({
6908
6892
  throw err;
6909
6893
  }
6910
6894
  } else if (sonic.mkdir) {
6911
- fs48.mkdir(path60.dirname(file), { recursive: true }, (err) => {
6895
+ fs48.mkdir(path59.dirname(file), { recursive: true }, (err) => {
6912
6896
  if (err) return fileOpened(err);
6913
6897
  fs48.open(file, flags, mode, fileOpened);
6914
6898
  });
@@ -7081,7 +7065,7 @@ var require_sonic_boom = __commonJS({
7081
7065
  sonic._asyncDrainScheduled = false;
7082
7066
  sonic.emit("drain");
7083
7067
  }
7084
- inherits(SonicBoom, EventEmitter2);
7068
+ inherits(SonicBoom, EventEmitter3);
7085
7069
  function mergeBuf(bufs, len) {
7086
7070
  if (bufs.length === 0) {
7087
7071
  return kEmptyBuffer;
@@ -7659,9 +7643,9 @@ var require_thread_stream = __commonJS({
7659
7643
  "../node_modules/.pnpm/thread-stream@3.1.0/node_modules/thread-stream/index.js"(exports2, module2) {
7660
7644
  "use strict";
7661
7645
  var { version: version2 } = require_package();
7662
- var { EventEmitter: EventEmitter2 } = require("events");
7646
+ var { EventEmitter: EventEmitter3 } = require("events");
7663
7647
  var { Worker } = require("worker_threads");
7664
- var { join: join11 } = require("path");
7648
+ var { join: join12 } = require("path");
7665
7649
  var { pathToFileURL } = require("url");
7666
7650
  var { wait } = require_wait();
7667
7651
  var {
@@ -7697,7 +7681,7 @@ var require_thread_stream = __commonJS({
7697
7681
  function createWorker(stream, opts) {
7698
7682
  const { filename, workerData } = opts;
7699
7683
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
7700
- const toExecute = bundlerOverrides["thread-stream-worker"] || join11(__dirname, "lib", "worker.js");
7684
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join12(__dirname, "lib", "worker.js");
7701
7685
  const worker = new Worker(toExecute, {
7702
7686
  ...opts.workerOpts,
7703
7687
  trackUnmanagedFds: false,
@@ -7815,7 +7799,7 @@ var require_thread_stream = __commonJS({
7815
7799
  stream.worker.off("exit", onWorkerExit);
7816
7800
  destroy(stream, code !== 0 ? new Error("the worker thread exited") : null);
7817
7801
  }
7818
- var ThreadStream = class extends EventEmitter2 {
7802
+ var ThreadStream = class extends EventEmitter3 {
7819
7803
  constructor(opts = {}) {
7820
7804
  super();
7821
7805
  if (opts.bufferSize < 4) {
@@ -8083,7 +8067,7 @@ var require_transport = __commonJS({
8083
8067
  "use strict";
8084
8068
  var { createRequire } = require("module");
8085
8069
  var getCallers = require_caller();
8086
- var { join: join11, isAbsolute: isAbsolute2, sep: sep3 } = require("path");
8070
+ var { join: join12, isAbsolute: isAbsolute2, sep: sep3 } = require("path");
8087
8071
  var sleep = require_atomic_sleep();
8088
8072
  var onExit = require_on_exit_leak_free();
8089
8073
  var ThreadStream = require_thread_stream();
@@ -8146,7 +8130,7 @@ var require_transport = __commonJS({
8146
8130
  throw new Error("only one of target or targets can be specified");
8147
8131
  }
8148
8132
  if (targets) {
8149
- target = bundlerOverrides["pino-worker"] || join11(__dirname, "worker.js");
8133
+ target = bundlerOverrides["pino-worker"] || join12(__dirname, "worker.js");
8150
8134
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
8151
8135
  return {
8152
8136
  ...dest,
@@ -8164,7 +8148,7 @@ var require_transport = __commonJS({
8164
8148
  });
8165
8149
  });
8166
8150
  } else if (pipeline3) {
8167
- target = bundlerOverrides["pino-worker"] || join11(__dirname, "worker.js");
8151
+ target = bundlerOverrides["pino-worker"] || join12(__dirname, "worker.js");
8168
8152
  options.pipelines = [pipeline3.map((dest) => {
8169
8153
  return {
8170
8154
  ...dest,
@@ -8186,7 +8170,7 @@ var require_transport = __commonJS({
8186
8170
  return origin;
8187
8171
  }
8188
8172
  if (origin === "pino/file") {
8189
- return join11(__dirname, "..", "file.js");
8173
+ return join12(__dirname, "..", "file.js");
8190
8174
  }
8191
8175
  let fixTarget2;
8192
8176
  for (const filePath of callers) {
@@ -8773,7 +8757,7 @@ var require_meta = __commonJS({
8773
8757
  var require_proto = __commonJS({
8774
8758
  "../node_modules/.pnpm/pino@9.14.0/node_modules/pino/lib/proto.js"(exports2, module2) {
8775
8759
  "use strict";
8776
- var { EventEmitter: EventEmitter2 } = require("events");
8760
+ var { EventEmitter: EventEmitter3 } = require("events");
8777
8761
  var {
8778
8762
  lsCacheSym,
8779
8763
  levelValSym,
@@ -8855,7 +8839,7 @@ var require_proto = __commonJS({
8855
8839
  [getLevelSym]: getLevel,
8856
8840
  [setLevelSym]: setLevel
8857
8841
  };
8858
- Object.setPrototypeOf(prototype, EventEmitter2.prototype);
8842
+ Object.setPrototypeOf(prototype, EventEmitter3.prototype);
8859
8843
  module2.exports = function() {
8860
8844
  return Object.create(prototype);
8861
8845
  };
@@ -9176,7 +9160,7 @@ var require_safe_stable_stringify = __commonJS({
9176
9160
  return circularValue;
9177
9161
  }
9178
9162
  let res = "";
9179
- let join11 = ",";
9163
+ let join12 = ",";
9180
9164
  const originalIndentation = indentation;
9181
9165
  if (Array.isArray(value)) {
9182
9166
  if (value.length === 0) {
@@ -9190,7 +9174,7 @@ var require_safe_stable_stringify = __commonJS({
9190
9174
  indentation += spacer;
9191
9175
  res += `
9192
9176
  ${indentation}`;
9193
- join11 = `,
9177
+ join12 = `,
9194
9178
  ${indentation}`;
9195
9179
  }
9196
9180
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -9198,13 +9182,13 @@ ${indentation}`;
9198
9182
  for (; i < maximumValuesToStringify - 1; i++) {
9199
9183
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
9200
9184
  res += tmp2 !== void 0 ? tmp2 : "null";
9201
- res += join11;
9185
+ res += join12;
9202
9186
  }
9203
9187
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
9204
9188
  res += tmp !== void 0 ? tmp : "null";
9205
9189
  if (value.length - 1 > maximumBreadth) {
9206
9190
  const removedKeys = value.length - maximumBreadth - 1;
9207
- res += `${join11}"... ${getItemCount(removedKeys)} not stringified"`;
9191
+ res += `${join12}"... ${getItemCount(removedKeys)} not stringified"`;
9208
9192
  }
9209
9193
  if (spacer !== "") {
9210
9194
  res += `
@@ -9225,7 +9209,7 @@ ${originalIndentation}`;
9225
9209
  let separator = "";
9226
9210
  if (spacer !== "") {
9227
9211
  indentation += spacer;
9228
- join11 = `,
9212
+ join12 = `,
9229
9213
  ${indentation}`;
9230
9214
  whitespace = " ";
9231
9215
  }
@@ -9239,13 +9223,13 @@ ${indentation}`;
9239
9223
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
9240
9224
  if (tmp !== void 0) {
9241
9225
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
9242
- separator = join11;
9226
+ separator = join12;
9243
9227
  }
9244
9228
  }
9245
9229
  if (keyLength > maximumBreadth) {
9246
9230
  const removedKeys = keyLength - maximumBreadth;
9247
9231
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
9248
- separator = join11;
9232
+ separator = join12;
9249
9233
  }
9250
9234
  if (spacer !== "" && separator.length > 1) {
9251
9235
  res = `
@@ -9286,7 +9270,7 @@ ${originalIndentation}`;
9286
9270
  }
9287
9271
  const originalIndentation = indentation;
9288
9272
  let res = "";
9289
- let join11 = ",";
9273
+ let join12 = ",";
9290
9274
  if (Array.isArray(value)) {
9291
9275
  if (value.length === 0) {
9292
9276
  return "[]";
@@ -9299,7 +9283,7 @@ ${originalIndentation}`;
9299
9283
  indentation += spacer;
9300
9284
  res += `
9301
9285
  ${indentation}`;
9302
- join11 = `,
9286
+ join12 = `,
9303
9287
  ${indentation}`;
9304
9288
  }
9305
9289
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -9307,13 +9291,13 @@ ${indentation}`;
9307
9291
  for (; i < maximumValuesToStringify - 1; i++) {
9308
9292
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
9309
9293
  res += tmp2 !== void 0 ? tmp2 : "null";
9310
- res += join11;
9294
+ res += join12;
9311
9295
  }
9312
9296
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
9313
9297
  res += tmp !== void 0 ? tmp : "null";
9314
9298
  if (value.length - 1 > maximumBreadth) {
9315
9299
  const removedKeys = value.length - maximumBreadth - 1;
9316
- res += `${join11}"... ${getItemCount(removedKeys)} not stringified"`;
9300
+ res += `${join12}"... ${getItemCount(removedKeys)} not stringified"`;
9317
9301
  }
9318
9302
  if (spacer !== "") {
9319
9303
  res += `
@@ -9326,7 +9310,7 @@ ${originalIndentation}`;
9326
9310
  let whitespace = "";
9327
9311
  if (spacer !== "") {
9328
9312
  indentation += spacer;
9329
- join11 = `,
9313
+ join12 = `,
9330
9314
  ${indentation}`;
9331
9315
  whitespace = " ";
9332
9316
  }
@@ -9335,7 +9319,7 @@ ${indentation}`;
9335
9319
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
9336
9320
  if (tmp !== void 0) {
9337
9321
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
9338
- separator = join11;
9322
+ separator = join12;
9339
9323
  }
9340
9324
  }
9341
9325
  if (spacer !== "" && separator.length > 1) {
@@ -9393,20 +9377,20 @@ ${originalIndentation}`;
9393
9377
  indentation += spacer;
9394
9378
  let res2 = `
9395
9379
  ${indentation}`;
9396
- const join12 = `,
9380
+ const join13 = `,
9397
9381
  ${indentation}`;
9398
9382
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
9399
9383
  let i = 0;
9400
9384
  for (; i < maximumValuesToStringify - 1; i++) {
9401
9385
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
9402
9386
  res2 += tmp2 !== void 0 ? tmp2 : "null";
9403
- res2 += join12;
9387
+ res2 += join13;
9404
9388
  }
9405
9389
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
9406
9390
  res2 += tmp !== void 0 ? tmp : "null";
9407
9391
  if (value.length - 1 > maximumBreadth) {
9408
9392
  const removedKeys = value.length - maximumBreadth - 1;
9409
- res2 += `${join12}"... ${getItemCount(removedKeys)} not stringified"`;
9393
+ res2 += `${join13}"... ${getItemCount(removedKeys)} not stringified"`;
9410
9394
  }
9411
9395
  res2 += `
9412
9396
  ${originalIndentation}`;
@@ -9422,16 +9406,16 @@ ${originalIndentation}`;
9422
9406
  return '"[Object]"';
9423
9407
  }
9424
9408
  indentation += spacer;
9425
- const join11 = `,
9409
+ const join12 = `,
9426
9410
  ${indentation}`;
9427
9411
  let res = "";
9428
9412
  let separator = "";
9429
9413
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
9430
9414
  if (isTypedArrayWithEntries(value)) {
9431
- res += stringifyTypedArray(value, join11, maximumBreadth);
9415
+ res += stringifyTypedArray(value, join12, maximumBreadth);
9432
9416
  keys = keys.slice(value.length);
9433
9417
  maximumPropertiesToStringify -= value.length;
9434
- separator = join11;
9418
+ separator = join12;
9435
9419
  }
9436
9420
  if (deterministic) {
9437
9421
  keys = sort(keys, comparator);
@@ -9442,13 +9426,13 @@ ${indentation}`;
9442
9426
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
9443
9427
  if (tmp !== void 0) {
9444
9428
  res += `${separator}${strEscape(key2)}: ${tmp}`;
9445
- separator = join11;
9429
+ separator = join12;
9446
9430
  }
9447
9431
  }
9448
9432
  if (keyLength > maximumBreadth) {
9449
9433
  const removedKeys = keyLength - maximumBreadth;
9450
9434
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
9451
- separator = join11;
9435
+ separator = join12;
9452
9436
  }
9453
9437
  if (separator !== "") {
9454
9438
  res = `
@@ -10539,11 +10523,11 @@ var init_lib = __esm({
10539
10523
  }
10540
10524
  }
10541
10525
  },
10542
- addToPath: function addToPath(path60, added, removed, oldPosInc, options) {
10543
- var last = path60.lastComponent;
10526
+ addToPath: function addToPath(path59, added, removed, oldPosInc, options) {
10527
+ var last = path59.lastComponent;
10544
10528
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
10545
10529
  return {
10546
- oldPos: path60.oldPos + oldPosInc,
10530
+ oldPos: path59.oldPos + oldPosInc,
10547
10531
  lastComponent: {
10548
10532
  count: last.count + 1,
10549
10533
  added,
@@ -10553,7 +10537,7 @@ var init_lib = __esm({
10553
10537
  };
10554
10538
  } else {
10555
10539
  return {
10556
- oldPos: path60.oldPos + oldPosInc,
10540
+ oldPos: path59.oldPos + oldPosInc,
10557
10541
  lastComponent: {
10558
10542
  count: 1,
10559
10543
  added,
@@ -10999,10 +10983,10 @@ function attachmentToHistoryMessage(o, ts) {
10999
10983
  const memories = raw.map((m2) => {
11000
10984
  if (!m2 || typeof m2 !== "object") return null;
11001
10985
  const rec = m2;
11002
- const path60 = typeof rec.path === "string" ? rec.path : null;
10986
+ const path59 = typeof rec.path === "string" ? rec.path : null;
11003
10987
  const content = typeof rec.content === "string" ? rec.content : null;
11004
- if (!path60 || content == null) return null;
11005
- const entry = { path: path60, content };
10988
+ if (!path59 || content == null) return null;
10989
+ const entry = { path: path59, content };
11006
10990
  if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
11007
10991
  return entry;
11008
10992
  }).filter((m2) => m2 !== null);
@@ -11833,10 +11817,10 @@ function parseAttachment(obj) {
11833
11817
  const memories = raw.map((m2) => {
11834
11818
  if (!m2 || typeof m2 !== "object") return null;
11835
11819
  const rec = m2;
11836
- const path60 = typeof rec.path === "string" ? rec.path : null;
11820
+ const path59 = typeof rec.path === "string" ? rec.path : null;
11837
11821
  const content = typeof rec.content === "string" ? rec.content : null;
11838
- if (!path60 || content == null) return null;
11839
- const out = { path: path60, content };
11822
+ if (!path59 || content == null) return null;
11823
+ const out = { path: path59, content };
11840
11824
  if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
11841
11825
  return out;
11842
11826
  }).filter((m2) => m2 !== null);
@@ -19330,10 +19314,10 @@ var require_extension = __commonJS({
19330
19314
  var require_websocket = __commonJS({
19331
19315
  "../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket.js"(exports2, module2) {
19332
19316
  "use strict";
19333
- var EventEmitter2 = require("events");
19317
+ var EventEmitter3 = require("events");
19334
19318
  var https = require("https");
19335
- var http2 = require("http");
19336
- var net2 = require("net");
19319
+ var http3 = require("http");
19320
+ var net3 = require("net");
19337
19321
  var tls = require("tls");
19338
19322
  var { randomBytes, createHash: createHash2 } = require("crypto");
19339
19323
  var { Duplex, Readable: Readable3 } = require("stream");
@@ -19362,7 +19346,7 @@ var require_websocket = __commonJS({
19362
19346
  var protocolVersions = [8, 13];
19363
19347
  var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
19364
19348
  var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
19365
- var WebSocket2 = class _WebSocket extends EventEmitter2 {
19349
+ var WebSocket2 = class _WebSocket extends EventEmitter3 {
19366
19350
  /**
19367
19351
  * Create a new `WebSocket`.
19368
19352
  *
@@ -19866,7 +19850,7 @@ var require_websocket = __commonJS({
19866
19850
  }
19867
19851
  const defaultPort = isSecure ? 443 : 80;
19868
19852
  const key = randomBytes(16).toString("base64");
19869
- const request = isSecure ? https.request : http2.request;
19853
+ const request = isSecure ? https.request : http3.request;
19870
19854
  const protocolSet = /* @__PURE__ */ new Set();
19871
19855
  let perMessageDeflate;
19872
19856
  opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
@@ -20067,12 +20051,12 @@ var require_websocket = __commonJS({
20067
20051
  }
20068
20052
  function netConnect(options) {
20069
20053
  options.path = options.socketPath;
20070
- return net2.connect(options);
20054
+ return net3.connect(options);
20071
20055
  }
20072
20056
  function tlsConnect(options) {
20073
20057
  options.path = void 0;
20074
20058
  if (!options.servername && options.servername !== "") {
20075
- options.servername = net2.isIP(options.host) ? "" : options.host;
20059
+ options.servername = net3.isIP(options.host) ? "" : options.host;
20076
20060
  }
20077
20061
  return tls.connect(options);
20078
20062
  }
@@ -20359,8 +20343,8 @@ var require_subprotocol = __commonJS({
20359
20343
  var require_websocket_server = __commonJS({
20360
20344
  "../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket-server.js"(exports2, module2) {
20361
20345
  "use strict";
20362
- var EventEmitter2 = require("events");
20363
- var http2 = require("http");
20346
+ var EventEmitter3 = require("events");
20347
+ var http3 = require("http");
20364
20348
  var { Duplex } = require("stream");
20365
20349
  var { createHash: createHash2 } = require("crypto");
20366
20350
  var extension2 = require_extension();
@@ -20372,7 +20356,7 @@ var require_websocket_server = __commonJS({
20372
20356
  var RUNNING = 0;
20373
20357
  var CLOSING = 1;
20374
20358
  var CLOSED = 2;
20375
- var WebSocketServer2 = class extends EventEmitter2 {
20359
+ var WebSocketServer2 = class extends EventEmitter3 {
20376
20360
  /**
20377
20361
  * Create a `WebSocketServer` instance.
20378
20362
  *
@@ -20435,8 +20419,8 @@ var require_websocket_server = __commonJS({
20435
20419
  );
20436
20420
  }
20437
20421
  if (options.port != null) {
20438
- this._server = http2.createServer((req, res) => {
20439
- const body = http2.STATUS_CODES[426];
20422
+ this._server = http3.createServer((req, res) => {
20423
+ const body = http3.STATUS_CODES[426];
20440
20424
  res.writeHead(426, {
20441
20425
  "Content-Length": body.length,
20442
20426
  "Content-Type": "text/plain"
@@ -20723,7 +20707,7 @@ var require_websocket_server = __commonJS({
20723
20707
  this.destroy();
20724
20708
  }
20725
20709
  function abortHandshake(socket, code, message, headers) {
20726
- message = message || http2.STATUS_CODES[code];
20710
+ message = message || http3.STATUS_CODES[code];
20727
20711
  headers = {
20728
20712
  Connection: "close",
20729
20713
  "Content-Type": "text/html",
@@ -20732,7 +20716,7 @@ var require_websocket_server = __commonJS({
20732
20716
  };
20733
20717
  socket.once("finish", socket.destroy);
20734
20718
  socket.end(
20735
- `HTTP/1.1 ${code} ${http2.STATUS_CODES[code]}\r
20719
+ `HTTP/1.1 ${code} ${http3.STATUS_CODES[code]}\r
20736
20720
  ` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
20737
20721
  );
20738
20722
  }
@@ -21035,7 +21019,7 @@ var require_BufferList = __commonJS({
21035
21019
  this.head = this.tail = null;
21036
21020
  this.length = 0;
21037
21021
  };
21038
- BufferList.prototype.join = function join11(s) {
21022
+ BufferList.prototype.join = function join12(s) {
21039
21023
  if (this.length === 0) return "";
21040
21024
  var p2 = this.head;
21041
21025
  var ret = "" + p2.data;
@@ -23625,8 +23609,8 @@ var require_utils = __commonJS({
23625
23609
  var result = transform[inputType][outputType](input);
23626
23610
  return result;
23627
23611
  };
23628
- exports2.resolve = function(path60) {
23629
- var parts = path60.split("/");
23612
+ exports2.resolve = function(path59) {
23613
+ var parts = path59.split("/");
23630
23614
  var result = [];
23631
23615
  for (var index = 0; index < parts.length; index++) {
23632
23616
  var part = parts[index];
@@ -29479,18 +29463,18 @@ var require_object = __commonJS({
29479
29463
  var object = new ZipObject(name, zipObjectContent, o);
29480
29464
  this.files[name] = object;
29481
29465
  };
29482
- var parentFolder = function(path60) {
29483
- if (path60.slice(-1) === "/") {
29484
- path60 = path60.substring(0, path60.length - 1);
29466
+ var parentFolder = function(path59) {
29467
+ if (path59.slice(-1) === "/") {
29468
+ path59 = path59.substring(0, path59.length - 1);
29485
29469
  }
29486
- var lastSlash = path60.lastIndexOf("/");
29487
- return lastSlash > 0 ? path60.substring(0, lastSlash) : "";
29470
+ var lastSlash = path59.lastIndexOf("/");
29471
+ return lastSlash > 0 ? path59.substring(0, lastSlash) : "";
29488
29472
  };
29489
- var forceTrailingSlash = function(path60) {
29490
- if (path60.slice(-1) !== "/") {
29491
- path60 += "/";
29473
+ var forceTrailingSlash = function(path59) {
29474
+ if (path59.slice(-1) !== "/") {
29475
+ path59 += "/";
29492
29476
  }
29493
- return path60;
29477
+ return path59;
29494
29478
  };
29495
29479
  var folderAdd = function(name, createFolders) {
29496
29480
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -30945,6 +30929,8 @@ function resolveConfig(opts) {
30945
30929
  const frpcBinary = env.CLAWD_FRPC_BIN ?? null;
30946
30930
  const rawMode = env.CLAWD_CC_MODE;
30947
30931
  const mode = DAEMON_MODE_VALUES.includes(rawMode ?? "") ? rawMode : "sdk";
30932
+ const filePreviewPorts = Array.isArray(fileCfg.previewPorts) ? fileCfg.previewPorts.map((n) => Number(n)).filter((n) => Number.isInteger(n) && n > 0) : null;
30933
+ 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
30934
  return {
30949
30935
  port,
30950
30936
  host,
@@ -30956,7 +30942,8 @@ function resolveConfig(opts) {
30956
30942
  authToken,
30957
30943
  noTunnelPersist,
30958
30944
  frpcBinary,
30959
- mode
30945
+ mode,
30946
+ previewPorts
30960
30947
  };
30961
30948
  }
30962
30949
  var HELP_TEXT = `clawd [options]
@@ -32995,6 +32982,7 @@ var SessionManager = class {
32995
32982
  iconKey: args.iconKey,
32996
32983
  forkedFromSessionId: args.forkedFromSessionId,
32997
32984
  ownerPersonaId: args.ownerPersonaId,
32985
+ appBuilderProject: args.appBuilderProject,
32998
32986
  creatorPrincipalId,
32999
32987
  ...creatorOwnerPrincipalId ? { creatorOwnerPrincipalId } : {},
33000
32988
  createdAt: iso,
@@ -34631,6 +34619,15 @@ var DEFAULT_PERSONAS = [
34631
34619
  model: "opus",
34632
34620
  iconKey: "assist",
34633
34621
  public: false
34622
+ },
34623
+ {
34624
+ // app-builder:全栈应用搭建师,绑 extension-kit 模板 + multi-project picker
34625
+ // spec: superpowers/specs/2026-06-01-app-builder-project-picker-design.md
34626
+ personaId: "persona-app-builder",
34627
+ label: "App Builder",
34628
+ model: "opus",
34629
+ iconKey: "assist",
34630
+ public: false
34634
34631
  }
34635
34632
  ];
34636
34633
  function findDefaultsRoot() {
@@ -36327,13 +36324,63 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
36327
36324
  var wrapper_default = import_websocket.default;
36328
36325
 
36329
36326
  // src/transport/local-ws-server.ts
36327
+ var import_node_http2 = __toESM(require("http"), 1);
36328
+
36329
+ // src/transport/preview-proxy.ts
36330
36330
  var import_node_http = __toESM(require("http"), 1);
36331
+ var import_node_net = __toESM(require("net"), 1);
36332
+ var PREVIEW_RE = /^\/preview\/(\d+)(?:\/.*)?$/;
36333
+ function matchPreviewPort(pathname) {
36334
+ const m2 = pathname.match(PREVIEW_RE);
36335
+ return m2 ? Number(m2[1]) : null;
36336
+ }
36337
+ function isPreviewPortAllowed(port, cfg) {
36338
+ return cfg.allowedPorts.includes(port);
36339
+ }
36340
+ function proxyPreviewHttp(req, res, port) {
36341
+ return new Promise((resolve6) => {
36342
+ const upstream = import_node_http.default.request(
36343
+ { host: "localhost", port, method: req.method, path: req.url, headers: req.headers },
36344
+ (upRes) => {
36345
+ res.writeHead(upRes.statusCode || 502, upRes.headers);
36346
+ upRes.pipe(res);
36347
+ upRes.on("end", () => resolve6());
36348
+ }
36349
+ );
36350
+ upstream.on("error", (e) => {
36351
+ if (!res.headersSent) {
36352
+ res.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
36353
+ }
36354
+ res.end(`preview upstream error: ${e.message}`);
36355
+ resolve6();
36356
+ });
36357
+ req.pipe(upstream);
36358
+ });
36359
+ }
36360
+ function proxyPreviewUpgrade(req, socket, head, port) {
36361
+ const upstream = import_node_net.default.connect(port, "localhost", () => {
36362
+ let raw = `${req.method} ${req.url} HTTP/1.1\r
36363
+ `;
36364
+ for (let i = 0; i < req.rawHeaders.length; i += 2) {
36365
+ raw += `${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r
36366
+ `;
36367
+ }
36368
+ raw += "\r\n";
36369
+ upstream.write(raw);
36370
+ if (head && head.length) upstream.write(head);
36371
+ upstream.pipe(socket);
36372
+ socket.pipe(upstream);
36373
+ });
36374
+ upstream.on("error", () => socket.destroy());
36375
+ socket.on("error", () => upstream.destroy());
36376
+ }
36377
+
36378
+ // src/transport/local-ws-server.ts
36331
36379
  var LocalWsServer = class {
36332
36380
  constructor(opts) {
36333
36381
  this.opts = opts;
36334
36382
  this.logger = opts.logger;
36335
36383
  this.pingIntervalMs = opts.pingIntervalMs ?? 3e4;
36336
- this.currentOwnerPrincipalId = opts.ownerPrincipalId;
36337
36384
  }
36338
36385
  opts;
36339
36386
  wss = null;
@@ -36345,19 +36392,28 @@ var LocalWsServer = class {
36345
36392
  capabilityIdToClients = /* @__PURE__ */ new Map();
36346
36393
  logger;
36347
36394
  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
36395
  async start() {
36356
36396
  const host = this.opts.host ?? "127.0.0.1";
36357
36397
  await new Promise((resolve6, reject) => {
36358
- const httpServer = import_node_http.default.createServer((req, res) => this.handleHttpRequest(req, res));
36398
+ const httpServer = import_node_http2.default.createServer((req, res) => this.handleHttpRequest(req, res));
36359
36399
  const wss = new import_websocket_server.default({ noServer: true, clientTracking: true });
36360
36400
  httpServer.on("upgrade", (req, socket, head) => {
36401
+ if (req.url?.startsWith("/preview/")) {
36402
+ const pathname = (() => {
36403
+ try {
36404
+ return new URL(req.url, "http://localhost").pathname;
36405
+ } catch {
36406
+ return "/";
36407
+ }
36408
+ })();
36409
+ const port = matchPreviewPort(pathname);
36410
+ if (port != null && (this.opts.previewPorts ?? []).includes(port)) {
36411
+ proxyPreviewUpgrade(req, socket, head, port);
36412
+ } else {
36413
+ socket.destroy();
36414
+ }
36415
+ return;
36416
+ }
36361
36417
  wss.handleUpgrade(req, socket, head, (ws) => {
36362
36418
  this.routeConnection(ws, req);
36363
36419
  });
@@ -36471,7 +36527,7 @@ var LocalWsServer = class {
36471
36527
  // principalId === 'cap_xxx' → 该 capability 的所有 guest ws (多端 / 多 tab)
36472
36528
  // 用于 DM inbox:event 路由: from + to 两侧各播一次, 让双方 UI 实时看到 thread 更新.
36473
36529
  broadcastToPrincipal(principalId, frame) {
36474
- if (principalId === "owner" || principalId === this.currentOwnerPrincipalId) {
36530
+ if (principalId === "owner" || principalId === this.opts.ownerPrincipalId) {
36475
36531
  this.broadcastToOwners(frame);
36476
36532
  return;
36477
36533
  }
@@ -36523,21 +36579,6 @@ var LocalWsServer = class {
36523
36579
  getClientContext(clientId) {
36524
36580
  return this.clients.get(clientId)?.ctx ?? null;
36525
36581
  }
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
36582
  // Task 1.10 撤销级联:关闭某 capability 的所有活跃 ws 连接。close code 4401
36542
36583
  // 让客户端识别 'capability revoked' 而非普通断连。
36543
36584
  closeConnectionsByCapability(capabilityId, code = 4401, reason = "TOKEN_REVOKED") {
@@ -38020,23 +38061,13 @@ function createHttpRouter(deps) {
38020
38061
  });
38021
38062
  return true;
38022
38063
  }
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}` });
38064
+ if (url.pathname.startsWith("/preview/")) {
38065
+ const port = matchPreviewPort(url.pathname);
38066
+ if (port != null && isPreviewPortAllowed(port, { allowedPorts: deps.previewPorts ?? [] })) {
38067
+ await proxyPreviewHttp(req, res, port);
38026
38068
  return true;
38027
38069
  }
38028
- if (!deps.feishuLogin) {
38029
- sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "feishu login not wired" });
38030
- return true;
38031
- }
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);
38070
+ sendJson(res, 403, { code: "PREVIEW_PORT_FORBIDDEN", message: "preview port not allowed" });
38040
38071
  return true;
38041
38072
  }
38042
38073
  if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/") && url.pathname !== "/attachments/upload" && url.pathname !== "/extensions/import") {
@@ -38274,9 +38305,6 @@ function parseUrl(rawUrl) {
38274
38305
  return null;
38275
38306
  }
38276
38307
  }
38277
- function escapeHtml(s) {
38278
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
38279
- }
38280
38308
  function sendJson(res, status, body, extraHeaders) {
38281
38309
  res.writeHead(status, {
38282
38310
  "Content-Type": "application/json; charset=utf-8",
@@ -39299,160 +39327,6 @@ function loadOwnerDisplayName(dataDir) {
39299
39327
  return displayName;
39300
39328
  }
39301
39329
 
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
39330
  // src/index.ts
39457
39331
  init_protocol();
39458
39332
 
@@ -39460,12 +39334,12 @@ init_protocol();
39460
39334
  init_protocol();
39461
39335
 
39462
39336
  // src/session/fork.ts
39463
- var import_node_fs28 = __toESM(require("fs"), 1);
39337
+ var import_node_fs27 = __toESM(require("fs"), 1);
39464
39338
  var import_node_os14 = __toESM(require("os"), 1);
39465
- var import_node_path32 = __toESM(require("path"), 1);
39339
+ var import_node_path31 = __toESM(require("path"), 1);
39466
39340
  init_claude_history();
39467
39341
  function readJsonlEntries(file) {
39468
- const raw = import_node_fs28.default.readFileSync(file, "utf8");
39342
+ const raw = import_node_fs27.default.readFileSync(file, "utf8");
39469
39343
  const out = [];
39470
39344
  for (const line of raw.split("\n")) {
39471
39345
  const t = line.trim();
@@ -39478,10 +39352,10 @@ function readJsonlEntries(file) {
39478
39352
  return out;
39479
39353
  }
39480
39354
  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)) {
39355
+ const baseDir = input.baseDir ?? import_node_path31.default.join(import_node_os14.default.homedir(), ".claude");
39356
+ const projectDir = import_node_path31.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
39357
+ const sourceFile = import_node_path31.default.join(projectDir, `${input.toolSessionId}.jsonl`);
39358
+ if (!import_node_fs27.default.existsSync(sourceFile)) {
39485
39359
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
39486
39360
  }
39487
39361
  const entries = readJsonlEntries(sourceFile);
@@ -39511,9 +39385,9 @@ function forkSession(input) {
39511
39385
  }
39512
39386
  forkedLines.push(JSON.stringify(forked));
39513
39387
  }
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 });
39388
+ const forkedFilePath = import_node_path31.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
39389
+ import_node_fs27.default.mkdirSync(projectDir, { recursive: true });
39390
+ import_node_fs27.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
39517
39391
  return { forkedToolSessionId, forkedFilePath };
39518
39392
  }
39519
39393
 
@@ -39865,7 +39739,7 @@ function buildPermissionHandlers(deps) {
39865
39739
  }
39866
39740
 
39867
39741
  // src/handlers/history.ts
39868
- var path42 = __toESM(require("path"), 1);
39742
+ var path41 = __toESM(require("path"), 1);
39869
39743
  init_protocol();
39870
39744
 
39871
39745
  // src/session/recent-dirs.ts
@@ -39883,7 +39757,7 @@ function listRecentDirs(store, limit = 50) {
39883
39757
  }
39884
39758
 
39885
39759
  // src/permission/persona-paths.ts
39886
- var path41 = __toESM(require("path"), 1);
39760
+ var path40 = __toESM(require("path"), 1);
39887
39761
  function getAllowedPersonaIds(grants, action) {
39888
39762
  const ids = /* @__PURE__ */ new Set();
39889
39763
  for (const g2 of grants) {
@@ -39896,26 +39770,26 @@ function getAllowedPersonaIds(grants, action) {
39896
39770
  return ids;
39897
39771
  }
39898
39772
  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;
39773
+ const root = path40.resolve(personaRoot);
39774
+ const target = path40.resolve(absPath);
39775
+ const sep3 = root.endsWith(path40.sep) ? "" : path40.sep;
39902
39776
  if (!target.startsWith(root + sep3)) return false;
39903
- const rel = path41.relative(root, target);
39777
+ const rel = path40.relative(root, target);
39904
39778
  if (!rel || rel.startsWith("..")) return false;
39905
- const personaId = rel.split(path41.sep)[0];
39779
+ const personaId = rel.split(path40.sep)[0];
39906
39780
  if (!personaId) return false;
39907
39781
  const allowed = getAllowedPersonaIds(grants, action);
39908
39782
  if (allowed === "*") return true;
39909
39783
  return allowed.has(personaId);
39910
39784
  }
39911
39785
  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;
39786
+ const root = path40.resolve(personaRoot);
39787
+ const target = path40.resolve(absPath);
39788
+ const sep3 = root.endsWith(path40.sep) ? "" : path40.sep;
39915
39789
  if (!target.startsWith(root + sep3)) return null;
39916
- const rel = path41.relative(root, target);
39790
+ const rel = path40.relative(root, target);
39917
39791
  if (!rel || rel.startsWith("..")) return null;
39918
- const id = rel.split(path41.sep)[0];
39792
+ const id = rel.split(path40.sep)[0];
39919
39793
  return id || null;
39920
39794
  }
39921
39795
 
@@ -39940,7 +39814,7 @@ function buildHistoryHandlers(deps) {
39940
39814
  if (!pid) return false;
39941
39815
  return isGuestPathAllowed(
39942
39816
  ctx.grants,
39943
- path42.join(personaRoot, pid),
39817
+ path41.join(personaRoot, pid),
39944
39818
  personaRoot,
39945
39819
  "read"
39946
39820
  );
@@ -39951,7 +39825,7 @@ function buildHistoryHandlers(deps) {
39951
39825
  };
39952
39826
  const list = async (frame, _client, ctx) => {
39953
39827
  const args = HistoryListArgs.parse(frame);
39954
- assertGuestPath(ctx, path42.resolve(args.projectPath), personaRoot, "history:list");
39828
+ assertGuestPath(ctx, path41.resolve(args.projectPath), personaRoot, "history:list");
39955
39829
  const sessions = await history.listSessions(args);
39956
39830
  return { response: { type: "history:list", sessions } };
39957
39831
  };
@@ -39983,13 +39857,13 @@ function buildHistoryHandlers(deps) {
39983
39857
  };
39984
39858
  const subagents = async (frame, _client, ctx) => {
39985
39859
  const args = HistorySubagentsArgs.parse(frame);
39986
- assertGuestPath(ctx, path42.resolve(args.cwd), personaRoot, "history:subagents");
39860
+ assertGuestPath(ctx, path41.resolve(args.cwd), personaRoot, "history:subagents");
39987
39861
  const subs = await history.listSubagents(args);
39988
39862
  return { response: { type: "history:subagents", subagents: subs } };
39989
39863
  };
39990
39864
  const subagentRead = async (frame, _client, ctx) => {
39991
39865
  const args = HistorySubagentReadArgs.parse(frame);
39992
- assertGuestPath(ctx, path42.resolve(args.cwd), personaRoot, "history:subagent-read");
39866
+ assertGuestPath(ctx, path41.resolve(args.cwd), personaRoot, "history:subagent-read");
39993
39867
  const res = await history.readSubagent(args);
39994
39868
  return { response: { type: "history:subagent-read", ...res } };
39995
39869
  };
@@ -39997,7 +39871,7 @@ function buildHistoryHandlers(deps) {
39997
39871
  const dirs = listRecentDirs(store);
39998
39872
  if (ctx?.principal.kind === "guest" && personaRoot) {
39999
39873
  const filtered = dirs.filter(
40000
- (d) => isGuestPathAllowed(ctx.grants, path42.resolve(d.cwd), personaRoot, "read")
39874
+ (d) => isGuestPathAllowed(ctx.grants, path41.resolve(d.cwd), personaRoot, "read")
40001
39875
  );
40002
39876
  return { response: { type: "history:recentDirs", dirs: filtered } };
40003
39877
  }
@@ -40014,7 +39888,7 @@ function buildHistoryHandlers(deps) {
40014
39888
  }
40015
39889
 
40016
39890
  // src/handlers/workspace.ts
40017
- var path43 = __toESM(require("path"), 1);
39891
+ var path42 = __toESM(require("path"), 1);
40018
39892
  var os15 = __toESM(require("os"), 1);
40019
39893
  init_protocol();
40020
39894
  init_protocol();
@@ -40055,22 +39929,22 @@ function buildWorkspaceHandlers(deps) {
40055
39929
  const args = WorkspaceListArgs.parse(frame);
40056
39930
  const isGuest = ctx?.principal.kind === "guest";
40057
39931
  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;
39932
+ const resolvedCwd = path42.resolve(args.cwd ?? fallbackCwd);
39933
+ const target = args.path ? path42.resolve(resolvedCwd, args.path) : resolvedCwd;
40060
39934
  assertGuestPath2(ctx, target, personaRoot, "workspace:list");
40061
39935
  const res = workspace.list({ ...args, cwd: resolvedCwd });
40062
39936
  return { response: { type: "workspace:list", ...res } };
40063
39937
  };
40064
39938
  const read = async (frame, _client, ctx) => {
40065
39939
  const args = WorkspaceReadArgs.parse(frame);
40066
- const target = path43.isAbsolute(args.path) ? path43.resolve(args.path) : path43.resolve(args.cwd, args.path);
39940
+ const target = path42.isAbsolute(args.path) ? path42.resolve(args.path) : path42.resolve(args.cwd, args.path);
40067
39941
  assertGuestPath2(ctx, target, personaRoot, "workspace:read");
40068
39942
  const res = workspace.read(args);
40069
39943
  return { response: { type: "workspace:read", ...res } };
40070
39944
  };
40071
39945
  const skillsList = async (frame, _client, ctx) => {
40072
39946
  const args = SkillsListArgs.parse(frame);
40073
- const cwdAbs = path43.resolve(args.cwd);
39947
+ const cwdAbs = path42.resolve(args.cwd);
40074
39948
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list");
40075
39949
  const list2 = skills.list(args);
40076
39950
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -40082,7 +39956,7 @@ function buildWorkspaceHandlers(deps) {
40082
39956
  };
40083
39957
  const agentsList = async (frame, _client, ctx) => {
40084
39958
  const args = AgentsListArgs.parse(frame);
40085
- const cwdAbs = path43.resolve(args.cwd);
39959
+ const cwdAbs = path42.resolve(args.cwd);
40086
39960
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list");
40087
39961
  const list2 = agents.list(args);
40088
39962
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -40101,20 +39975,20 @@ function buildWorkspaceHandlers(deps) {
40101
39975
  }
40102
39976
 
40103
39977
  // src/handlers/git.ts
40104
- var path45 = __toESM(require("path"), 1);
39978
+ var path44 = __toESM(require("path"), 1);
40105
39979
  init_protocol();
40106
39980
  init_protocol();
40107
39981
 
40108
39982
  // src/workspace/git.ts
40109
39983
  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);
39984
+ var import_node_fs28 = __toESM(require("fs"), 1);
39985
+ var import_node_path32 = __toESM(require("path"), 1);
40112
39986
  var import_node_util = require("util");
40113
39987
  var pexec = (0, import_node_util.promisify)(import_node_child_process6.execFile);
40114
39988
  function normalizePath(p2) {
40115
- const resolved = import_node_path33.default.resolve(p2);
39989
+ const resolved = import_node_path32.default.resolve(p2);
40116
39990
  try {
40117
- return import_node_fs29.default.realpathSync(resolved);
39991
+ return import_node_fs28.default.realpathSync(resolved);
40118
39992
  } catch {
40119
39993
  return resolved;
40120
39994
  }
@@ -40187,7 +40061,7 @@ async function listGitBranches(cwd) {
40187
40061
  // src/handlers/git.ts
40188
40062
  function assertGuestCwd(ctx, cwd, personaRoot, method) {
40189
40063
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
40190
- if (!isGuestPathAllowed(ctx.grants, path45.resolve(cwd), personaRoot, "read")) {
40064
+ if (!isGuestPathAllowed(ctx.grants, path44.resolve(cwd), personaRoot, "read")) {
40191
40065
  throw new ClawdError(
40192
40066
  ERROR_CODES.UNAUTHORIZED,
40193
40067
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -40549,36 +40423,6 @@ function buildWhoamiHandler(deps) {
40549
40423
  };
40550
40424
  }
40551
40425
 
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
40426
  // src/handlers/meta.ts
40583
40427
  var import_node_os15 = __toESM(require("os"), 1);
40584
40428
  init_protocol();
@@ -40706,7 +40550,7 @@ function buildPersonaHandlers(deps) {
40706
40550
  }
40707
40551
 
40708
40552
  // src/handlers/attachment.ts
40709
- var import_node_path34 = __toESM(require("path"), 1);
40553
+ var import_node_path33 = __toESM(require("path"), 1);
40710
40554
  init_protocol();
40711
40555
  init_protocol();
40712
40556
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -40760,12 +40604,12 @@ function buildAttachmentHandlers(deps) {
40760
40604
  `session ${args.sessionId} scope unresolved`
40761
40605
  );
40762
40606
  }
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);
40607
+ const cwdAbs = import_node_path33.default.resolve(sessionFile.cwd);
40608
+ 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
40609
  assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl");
40766
40610
  const entries = deps.groupFileStore.list(scope, args.sessionId);
40767
40611
  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);
40612
+ 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
40613
  return storedAbs === candidateAbs && !e.stale;
40770
40614
  });
40771
40615
  if (!entry) {
@@ -40789,7 +40633,7 @@ function buildAttachmentHandlers(deps) {
40789
40633
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
40790
40634
  const f = deps.sessionStore.read(sessionId);
40791
40635
  if (!f) return;
40792
- assertGuestAttachmentPath(ctx, import_node_path34.default.resolve(f.cwd), deps.personaRoot, method);
40636
+ assertGuestAttachmentPath(ctx, import_node_path33.default.resolve(f.cwd), deps.personaRoot, method);
40793
40637
  }
40794
40638
  const groupAdd = async (frame, _client, ctx) => {
40795
40639
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -40861,19 +40705,19 @@ function buildAttachmentHandlers(deps) {
40861
40705
 
40862
40706
  // src/handlers/extension.ts
40863
40707
  var import_promises7 = __toESM(require("fs/promises"), 1);
40864
- var import_node_path39 = __toESM(require("path"), 1);
40708
+ var import_node_path38 = __toESM(require("path"), 1);
40865
40709
  init_protocol();
40866
40710
 
40867
40711
  // src/extension/bundle-zip.ts
40868
40712
  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);
40713
+ var import_node_path34 = __toESM(require("path"), 1);
40714
+ var import_node_crypto11 = __toESM(require("crypto"), 1);
40871
40715
  var import_jszip2 = __toESM(require_lib3(), 1);
40872
40716
  async function bundleExtensionDir(dir) {
40873
40717
  const entries = await listFilesSorted(dir);
40874
40718
  const zip = new import_jszip2.default();
40875
40719
  for (const rel of entries) {
40876
- const abs = import_node_path35.default.join(dir, rel);
40720
+ const abs = import_node_path34.default.join(dir, rel);
40877
40721
  const content = await import_promises4.default.readFile(abs);
40878
40722
  zip.file(rel, content, { date: FIXED_DATE });
40879
40723
  }
@@ -40882,7 +40726,7 @@ async function bundleExtensionDir(dir) {
40882
40726
  compression: "DEFLATE",
40883
40727
  compressionOptions: { level: 6 }
40884
40728
  });
40885
- const sha256 = import_node_crypto12.default.createHash("sha256").update(buffer).digest("hex");
40729
+ const sha256 = import_node_crypto11.default.createHash("sha256").update(buffer).digest("hex");
40886
40730
  return { buffer, sha256 };
40887
40731
  }
40888
40732
  var FIXED_DATE = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
@@ -40894,7 +40738,7 @@ async function listFilesSorted(rootDir) {
40894
40738
  return out;
40895
40739
  }
40896
40740
  async function walk(absRoot, relPrefix, out) {
40897
- const dirAbs = import_node_path35.default.join(absRoot, relPrefix);
40741
+ const dirAbs = import_node_path34.default.join(absRoot, relPrefix);
40898
40742
  const entries = await import_promises4.default.readdir(dirAbs, { withFileTypes: true });
40899
40743
  for (const e of entries) {
40900
40744
  if (IGNORE_BASENAMES.has(e.name)) continue;
@@ -40948,25 +40792,25 @@ function computePublishCheck(args) {
40948
40792
 
40949
40793
  // src/extension/install-flow.ts
40950
40794
  var import_promises5 = __toESM(require("fs/promises"), 1);
40951
- var import_node_path37 = __toESM(require("path"), 1);
40795
+ var import_node_path36 = __toESM(require("path"), 1);
40952
40796
  var import_node_os17 = __toESM(require("os"), 1);
40953
- var import_node_crypto13 = __toESM(require("crypto"), 1);
40797
+ var import_node_crypto12 = __toESM(require("crypto"), 1);
40954
40798
  var import_jszip3 = __toESM(require_lib3(), 1);
40955
40799
 
40956
40800
  // src/extension/paths.ts
40957
40801
  var import_node_os16 = __toESM(require("os"), 1);
40958
- var import_node_path36 = __toESM(require("path"), 1);
40802
+ var import_node_path35 = __toESM(require("path"), 1);
40959
40803
  function clawdHomeRoot(override) {
40960
- return override ?? process.env.CLAWD_HOME ?? import_node_path36.default.join(import_node_os16.default.homedir(), ".clawd");
40804
+ return override ?? process.env.CLAWD_HOME ?? import_node_path35.default.join(import_node_os16.default.homedir(), ".clawd");
40961
40805
  }
40962
40806
  function extensionsRoot(override) {
40963
- return import_node_path36.default.join(clawdHomeRoot(override), "extensions");
40807
+ return import_node_path35.default.join(clawdHomeRoot(override), "extensions");
40964
40808
  }
40965
40809
  function publishedChannelsFile(override) {
40966
- return import_node_path36.default.join(clawdHomeRoot(override), "extensions-published.json");
40810
+ return import_node_path35.default.join(clawdHomeRoot(override), "extensions-published.json");
40967
40811
  }
40968
40812
  function bundleCacheRoot(override) {
40969
- return import_node_path36.default.join(clawdHomeRoot(override), "extension-bundles");
40813
+ return import_node_path35.default.join(clawdHomeRoot(override), "extension-bundles");
40970
40814
  }
40971
40815
 
40972
40816
  // src/extension/install-flow.ts
@@ -40979,7 +40823,7 @@ var InstallError = class extends Error {
40979
40823
  };
40980
40824
  async function installFromChannel(args, deps) {
40981
40825
  const { channelRef, snapshotHash, bundleZip } = args;
40982
- const computed = import_node_crypto13.default.createHash("sha256").update(bundleZip).digest("hex");
40826
+ const computed = import_node_crypto12.default.createHash("sha256").update(bundleZip).digest("hex");
40983
40827
  if (computed !== snapshotHash) {
40984
40828
  throw new InstallError(
40985
40829
  "HASH_MISMATCH",
@@ -40993,7 +40837,7 @@ async function installFromChannel(args, deps) {
40993
40837
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
40994
40838
  }
40995
40839
  for (const name of Object.keys(zip.files)) {
40996
- if (name.includes("..") || name.startsWith("/") || import_node_path37.default.isAbsolute(name)) {
40840
+ if (name.includes("..") || name.startsWith("/") || import_node_path36.default.isAbsolute(name)) {
40997
40841
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
40998
40842
  }
40999
40843
  }
@@ -41025,7 +40869,7 @@ async function installFromChannel(args, deps) {
41025
40869
  );
41026
40870
  }
41027
40871
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
41028
- const destDir = import_node_path37.default.join(deps.extensionsRoot, localExtId);
40872
+ const destDir = import_node_path36.default.join(deps.extensionsRoot, localExtId);
41029
40873
  let destExists = false;
41030
40874
  try {
41031
40875
  await import_promises5.default.access(destDir);
@@ -41039,16 +40883,16 @@ async function installFromChannel(args, deps) {
41039
40883
  );
41040
40884
  }
41041
40885
  const stage = await import_promises5.default.mkdtemp(
41042
- import_node_path37.default.join(import_node_os17.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
40886
+ import_node_path36.default.join(import_node_os17.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
41043
40887
  );
41044
40888
  try {
41045
40889
  for (const [name, entry] of Object.entries(zip.files)) {
41046
- const dest = import_node_path37.default.join(stage, name);
40890
+ const dest = import_node_path36.default.join(stage, name);
41047
40891
  if (entry.dir) {
41048
40892
  await import_promises5.default.mkdir(dest, { recursive: true });
41049
40893
  continue;
41050
40894
  }
41051
- await import_promises5.default.mkdir(import_node_path37.default.dirname(dest), { recursive: true });
40895
+ await import_promises5.default.mkdir(import_node_path36.default.dirname(dest), { recursive: true });
41052
40896
  if (name === "manifest.json") {
41053
40897
  const rewritten = { ...parsed.data, id: localExtId };
41054
40898
  await import_promises5.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -41069,9 +40913,9 @@ async function installFromChannel(args, deps) {
41069
40913
 
41070
40914
  // src/extension/update-flow.ts
41071
40915
  var import_promises6 = __toESM(require("fs/promises"), 1);
41072
- var import_node_path38 = __toESM(require("path"), 1);
40916
+ var import_node_path37 = __toESM(require("path"), 1);
41073
40917
  var import_node_os18 = __toESM(require("os"), 1);
41074
- var import_node_crypto14 = __toESM(require("crypto"), 1);
40918
+ var import_node_crypto13 = __toESM(require("crypto"), 1);
41075
40919
  var import_jszip4 = __toESM(require_lib3(), 1);
41076
40920
  var UpdateError = class extends Error {
41077
40921
  constructor(code, message) {
@@ -41086,11 +40930,11 @@ async function updateFromChannel(args, deps) {
41086
40930
  channelRef.extId,
41087
40931
  channelRef.ownerPrincipalId
41088
40932
  );
41089
- const liveDir = import_node_path38.default.join(deps.extensionsRoot, localExtId);
40933
+ const liveDir = import_node_path37.default.join(deps.extensionsRoot, localExtId);
41090
40934
  const prevDir = `${liveDir}.prev`;
41091
40935
  let existingVersion;
41092
40936
  try {
41093
- const raw = await import_promises6.default.readFile(import_node_path38.default.join(liveDir, "manifest.json"), "utf8");
40937
+ const raw = await import_promises6.default.readFile(import_node_path37.default.join(liveDir, "manifest.json"), "utf8");
41094
40938
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
41095
40939
  if (!parsed2.success) {
41096
40940
  throw new UpdateError(
@@ -41109,7 +40953,7 @@ async function updateFromChannel(args, deps) {
41109
40953
  if (e instanceof UpdateError) throw e;
41110
40954
  throw e;
41111
40955
  }
41112
- const computed = import_node_crypto14.default.createHash("sha256").update(bundleZip).digest("hex");
40956
+ const computed = import_node_crypto13.default.createHash("sha256").update(bundleZip).digest("hex");
41113
40957
  if (computed !== snapshotHash) {
41114
40958
  throw new UpdateError(
41115
40959
  "HASH_MISMATCH",
@@ -41123,7 +40967,7 @@ async function updateFromChannel(args, deps) {
41123
40967
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
41124
40968
  }
41125
40969
  for (const name of Object.keys(zip.files)) {
41126
- if (name.includes("..") || name.startsWith("/") || import_node_path38.default.isAbsolute(name)) {
40970
+ if (name.includes("..") || name.startsWith("/") || import_node_path37.default.isAbsolute(name)) {
41127
40971
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
41128
40972
  }
41129
40973
  }
@@ -41158,16 +41002,16 @@ async function updateFromChannel(args, deps) {
41158
41002
  await import_promises6.default.rm(prevDir, { recursive: true, force: true });
41159
41003
  await import_promises6.default.rename(liveDir, prevDir);
41160
41004
  const stage = await import_promises6.default.mkdtemp(
41161
- import_node_path38.default.join(import_node_os18.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
41005
+ import_node_path37.default.join(import_node_os18.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
41162
41006
  );
41163
41007
  try {
41164
41008
  for (const [name, entry] of Object.entries(zip.files)) {
41165
- const dest = import_node_path38.default.join(stage, name);
41009
+ const dest = import_node_path37.default.join(stage, name);
41166
41010
  if (entry.dir) {
41167
41011
  await import_promises6.default.mkdir(dest, { recursive: true });
41168
41012
  continue;
41169
41013
  }
41170
- await import_promises6.default.mkdir(import_node_path38.default.dirname(dest), { recursive: true });
41014
+ await import_promises6.default.mkdir(import_node_path37.default.dirname(dest), { recursive: true });
41171
41015
  if (name === "manifest.json") {
41172
41016
  const rewritten = { ...parsed.data, id: localExtId };
41173
41017
  await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -41260,7 +41104,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
41260
41104
  );
41261
41105
  }
41262
41106
  }
41263
- const manifestPath = import_node_path39.default.join(root, extId, "manifest.json");
41107
+ const manifestPath = import_node_path38.default.join(root, extId, "manifest.json");
41264
41108
  const manifest = await readManifest(root, extId);
41265
41109
  const next = { ...manifest, version: newVersion };
41266
41110
  const tmp = `${manifestPath}.tmp`;
@@ -41268,7 +41112,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
41268
41112
  await import_promises7.default.rename(tmp, manifestPath);
41269
41113
  }
41270
41114
  async function readManifest(root, extId) {
41271
- const file = import_node_path39.default.join(root, extId, "manifest.json");
41115
+ const file = import_node_path38.default.join(root, extId, "manifest.json");
41272
41116
  let raw;
41273
41117
  try {
41274
41118
  raw = await import_promises7.default.readFile(file, "utf8");
@@ -41359,7 +41203,7 @@ function buildExtensionHandlers(deps) {
41359
41203
  };
41360
41204
  async function buildSnapshotMeta(extId) {
41361
41205
  const manifest = await readManifest(deps.root, extId);
41362
- const { sha256, buffer } = await bundleExtensionDir(import_node_path39.default.join(deps.root, extId));
41206
+ const { sha256, buffer } = await bundleExtensionDir(import_node_path38.default.join(deps.root, extId));
41363
41207
  return { manifest, contentHash: sha256, buffer };
41364
41208
  }
41365
41209
  const publish = async (frame, _client, ctx) => {
@@ -41539,9 +41383,64 @@ function buildExtensionHandlers(deps) {
41539
41383
  };
41540
41384
  }
41541
41385
 
41386
+ // src/handlers/app-builder.ts
41387
+ function buildAppBuilderHandlers(deps) {
41388
+ const { store, deleteSessionsByProject, devServerLifecycle } = deps;
41389
+ const listProjects = async () => {
41390
+ const projects = await store.list();
41391
+ return {
41392
+ response: {
41393
+ projects: projects.map((p2) => ({
41394
+ ...p2,
41395
+ isRunning: devServerLifecycle.isRunning(p2.name),
41396
+ boundSessionId: devServerLifecycle.boundSessionId(p2.name)
41397
+ }))
41398
+ }
41399
+ };
41400
+ };
41401
+ const createProject = async (frame) => {
41402
+ const name = frame.name;
41403
+ if (typeof name !== "string") throw new Error("createProject: name must be string");
41404
+ const project = await store.create(name);
41405
+ return { response: { project } };
41406
+ };
41407
+ const deleteProject = async (frame) => {
41408
+ const name = frame.name;
41409
+ if (typeof name !== "string") throw new Error("deleteProject: name must be string");
41410
+ await devServerLifecycle.stopForProject(name);
41411
+ await deleteSessionsByProject(name);
41412
+ await store.delete(name);
41413
+ return { response: {} };
41414
+ };
41415
+ const updateProjectPort = async (frame) => {
41416
+ const f = frame;
41417
+ if (typeof f.name !== "string") throw new Error("updateProjectPort: name must be string");
41418
+ if (typeof f.newPort !== "number") throw new Error("updateProjectPort: newPort must be number");
41419
+ const entry = devServerLifecycle.boundEntry(f.name);
41420
+ await devServerLifecycle.stopForProject(f.name);
41421
+ const project = await store.updatePort(f.name, f.newPort);
41422
+ if (entry) {
41423
+ await devServerLifecycle.restartForProjectAt({
41424
+ projectName: f.name,
41425
+ newPort: f.newPort,
41426
+ sessionId: entry.sessionId,
41427
+ cwd: entry.cwd,
41428
+ tunnelHost: entry.tunnelHost
41429
+ });
41430
+ }
41431
+ return { response: { project } };
41432
+ };
41433
+ return {
41434
+ "appBuilder:listProjects": listProjects,
41435
+ "appBuilder:createProject": createProject,
41436
+ "appBuilder:deleteProject": deleteProject,
41437
+ "appBuilder:updateProjectPort": updateProjectPort
41438
+ };
41439
+ }
41440
+
41542
41441
  // src/extension/registry.ts
41543
41442
  var import_promises8 = __toESM(require("fs/promises"), 1);
41544
- var import_node_path40 = __toESM(require("path"), 1);
41443
+ var import_node_path39 = __toESM(require("path"), 1);
41545
41444
  async function loadAll(root) {
41546
41445
  let entries;
41547
41446
  try {
@@ -41554,13 +41453,13 @@ async function loadAll(root) {
41554
41453
  for (const ent of entries) {
41555
41454
  if (!ent.isDirectory()) continue;
41556
41455
  if (ent.name.startsWith(".")) continue;
41557
- records.push(await loadOne(import_node_path40.default.join(root, ent.name), ent.name));
41456
+ records.push(await loadOne(import_node_path39.default.join(root, ent.name), ent.name));
41558
41457
  }
41559
41458
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
41560
41459
  return records;
41561
41460
  }
41562
41461
  async function loadOne(dir, dirName) {
41563
- const manifestPath = import_node_path40.default.join(dir, "manifest.json");
41462
+ const manifestPath = import_node_path39.default.join(dir, "manifest.json");
41564
41463
  let raw;
41565
41464
  try {
41566
41465
  raw = await import_promises8.default.readFile(manifestPath, "utf8");
@@ -41605,7 +41504,7 @@ async function loadOne(dir, dirName) {
41605
41504
 
41606
41505
  // src/extension/uninstall.ts
41607
41506
  var import_promises9 = __toESM(require("fs/promises"), 1);
41608
- var import_node_path41 = __toESM(require("path"), 1);
41507
+ var import_node_path40 = __toESM(require("path"), 1);
41609
41508
  var UninstallError = class extends Error {
41610
41509
  constructor(code, message) {
41611
41510
  super(message);
@@ -41614,7 +41513,7 @@ var UninstallError = class extends Error {
41614
41513
  code;
41615
41514
  };
41616
41515
  async function uninstall(deps) {
41617
- const dir = import_node_path41.default.join(deps.root, deps.extId);
41516
+ const dir = import_node_path40.default.join(deps.root, deps.extId);
41618
41517
  try {
41619
41518
  await import_promises9.default.access(dir);
41620
41519
  } catch {
@@ -41660,7 +41559,6 @@ function buildMethodHandlers(deps) {
41660
41559
  personaStore: deps.personaStore,
41661
41560
  capabilityManager: deps.capabilityManager
41662
41561
  }),
41663
- ...buildFeishuAuthHandlers(deps.feishuAuth),
41664
41562
  ...deps.attachment ? buildAttachmentHandlers(deps.attachment) : {},
41665
41563
  ...buildExtensionHandlers({
41666
41564
  loadAll,
@@ -41669,10 +41567,186 @@ function buildMethodHandlers(deps) {
41669
41567
  root: extensionsRoot(),
41670
41568
  publishedChannelStore: deps.publishedChannelStore,
41671
41569
  bundleCache: deps.bundleCache
41570
+ }),
41571
+ ...buildAppBuilderHandlers({
41572
+ store: deps.appBuilderStore,
41573
+ deleteSessionsByProject: deps.deleteSessionsByProject,
41574
+ devServerLifecycle: deps.devServerLifecycle
41672
41575
  })
41673
41576
  };
41674
41577
  }
41675
41578
 
41579
+ // src/app-builder/project-store.ts
41580
+ var import_node_fs29 = require("fs");
41581
+ var import_node_path41 = require("path");
41582
+ init_protocol();
41583
+ var PROJECTS_DIR = "projects";
41584
+ var META_FILE = ".clawd-project.json";
41585
+ var ProjectStore = class {
41586
+ /** @param personaRoot persona 目录,例如 `~/.clawd/personas/persona-app-builder/` */
41587
+ constructor(personaRoot) {
41588
+ this.personaRoot = personaRoot;
41589
+ }
41590
+ personaRoot;
41591
+ /** projects/<name>/.clawd-project.json 路径 */
41592
+ metaPath(name) {
41593
+ return (0, import_node_path41.join)(this.projectsRoot(), name, META_FILE);
41594
+ }
41595
+ /** projects/<name>/ 目录路径(cwd 用) */
41596
+ projectDir(name) {
41597
+ return (0, import_node_path41.join)(this.projectsRoot(), name);
41598
+ }
41599
+ projectsRoot() {
41600
+ return (0, import_node_path41.join)(this.personaRoot, PROJECTS_DIR);
41601
+ }
41602
+ async list() {
41603
+ let entries;
41604
+ try {
41605
+ entries = await import_node_fs29.promises.readdir(this.projectsRoot());
41606
+ } catch (err) {
41607
+ if (err.code === "ENOENT") return [];
41608
+ throw err;
41609
+ }
41610
+ const out = [];
41611
+ for (const name of entries) {
41612
+ try {
41613
+ const raw = await import_node_fs29.promises.readFile(this.metaPath(name), "utf8");
41614
+ const parsed = ProjectMetadataSchema.safeParse(JSON.parse(raw));
41615
+ if (parsed.success) out.push(parsed.data);
41616
+ } catch {
41617
+ }
41618
+ }
41619
+ return out.sort((a, b2) => a.name.localeCompare(b2.name));
41620
+ }
41621
+ async create(name) {
41622
+ const existing = await this.list();
41623
+ if (existing.some((p2) => p2.name === name)) {
41624
+ throw new Error(`project "${name}" already exists / \u5DF2\u5B58\u5728`);
41625
+ }
41626
+ const usedPorts = new Set(existing.map((p2) => p2.port));
41627
+ let port = -1;
41628
+ for (let p2 = PROJECT_PORT_MIN; p2 <= PROJECT_PORT_MAX; p2++) {
41629
+ if (!usedPorts.has(p2)) {
41630
+ port = p2;
41631
+ break;
41632
+ }
41633
+ }
41634
+ if (port < 0) {
41635
+ throw new Error(
41636
+ `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`
41637
+ );
41638
+ }
41639
+ const meta = {
41640
+ name,
41641
+ port,
41642
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
41643
+ };
41644
+ const validated = ProjectMetadataSchema.safeParse(meta);
41645
+ if (!validated.success) {
41646
+ throw new Error(`invalid name "${name}": ${validated.error.message}`);
41647
+ }
41648
+ const dir = this.projectDir(name);
41649
+ await import_node_fs29.promises.mkdir(dir, { recursive: true });
41650
+ await import_node_fs29.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
41651
+ return meta;
41652
+ }
41653
+ async delete(name) {
41654
+ const dir = this.projectDir(name);
41655
+ await import_node_fs29.promises.rm(dir, { recursive: true, force: true });
41656
+ }
41657
+ async updatePort(name, newPort) {
41658
+ if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
41659
+ throw new Error(
41660
+ `port range invalid: must be ${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}, got ${newPort}`
41661
+ );
41662
+ }
41663
+ const list = await this.list();
41664
+ const target = list.find((p2) => p2.name === name);
41665
+ if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
41666
+ const conflict = list.find((p2) => p2.name !== name && p2.port === newPort);
41667
+ if (conflict) {
41668
+ throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
41669
+ }
41670
+ const updated = { ...target, port: newPort };
41671
+ await import_node_fs29.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
41672
+ return updated;
41673
+ }
41674
+ };
41675
+
41676
+ // src/app-builder/dev-server-supervisor.ts
41677
+ var import_node_child_process7 = require("child_process");
41678
+ var import_node_events2 = require("events");
41679
+ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
41680
+ running = /* @__PURE__ */ new Map();
41681
+ // key: sessionId
41682
+ startForSession(args) {
41683
+ if (this.running.has(args.sessionId)) return;
41684
+ const env = {
41685
+ ...process.env,
41686
+ CLAWD_TUNNEL_HOST: args.tunnelHost ?? "",
41687
+ CLAWD_PREVIEW_PORT: String(args.port)
41688
+ };
41689
+ const child = (0, import_node_child_process7.spawn)("pnpm", ["dev"], { cwd: args.cwd, env, stdio: "pipe" });
41690
+ const entry = { ...args, process: child };
41691
+ this.running.set(args.sessionId, entry);
41692
+ child.on("exit", (code) => {
41693
+ this.running.delete(args.sessionId);
41694
+ if (code !== 0 && code !== null) {
41695
+ const ev = {
41696
+ sessionId: args.sessionId,
41697
+ projectName: args.projectName,
41698
+ port: args.port,
41699
+ exitCode: code
41700
+ };
41701
+ this.emit("devServer:failed", ev);
41702
+ }
41703
+ });
41704
+ }
41705
+ stopForSession(sessionId) {
41706
+ const entry = this.running.get(sessionId);
41707
+ if (!entry) return Promise.resolve();
41708
+ return new Promise((resolve6) => {
41709
+ entry.process.once("exit", () => {
41710
+ this.running.delete(sessionId);
41711
+ resolve6();
41712
+ });
41713
+ entry.process.kill("SIGTERM");
41714
+ });
41715
+ }
41716
+ async stopForProject(projectName) {
41717
+ const entry = [...this.running.values()].find((e) => e.projectName === projectName);
41718
+ if (!entry) return;
41719
+ await this.stopForSession(entry.sessionId);
41720
+ }
41721
+ async restartForProjectAt(args) {
41722
+ await this.stopForSession(args.sessionId);
41723
+ this.startForSession({
41724
+ sessionId: args.sessionId,
41725
+ projectName: args.projectName,
41726
+ port: args.newPort,
41727
+ cwd: args.cwd,
41728
+ tunnelHost: args.tunnelHost
41729
+ });
41730
+ }
41731
+ isRunning(projectName) {
41732
+ return [...this.running.values()].some((e) => e.projectName === projectName);
41733
+ }
41734
+ boundSessionId(projectName) {
41735
+ const entry = [...this.running.values()].find((e) => e.projectName === projectName);
41736
+ return entry?.sessionId ?? null;
41737
+ }
41738
+ boundEntry(projectName) {
41739
+ const entry = [...this.running.values()].find((e) => e.projectName === projectName);
41740
+ if (!entry) return null;
41741
+ return {
41742
+ sessionId: entry.sessionId,
41743
+ cwd: entry.cwd,
41744
+ port: entry.port,
41745
+ tunnelHost: entry.tunnelHost
41746
+ };
41747
+ }
41748
+ };
41749
+
41676
41750
  // src/handlers/method-grants.ts
41677
41751
  var ADMIN_ANY = {
41678
41752
  kind: "fixed",
@@ -41803,11 +41877,14 @@ var METHOD_GRANT_MAP = {
41803
41877
  // guest-self:guest 在自己 daemon 上触发安装与更新(无持久化 subscription 概念)
41804
41878
  "extension.install-from-channel": ADMIN_ANY,
41805
41879
  "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
41880
+ // ---- app-builder Project picker(spec 2026-06-01-app-builder-project-picker-design) ----
41881
+ // owner-only:picker UI 给 owner 管理本机 build 中的 project(fs + 端口分配)。
41882
+ // 即使 persona-app-builder 是 PreinstalledPersona,project 管理也属本机 owner 行为,
41883
+ // 不暴露给 guest(远端接入者)。
41884
+ "appBuilder:listProjects": ADMIN_ANY,
41885
+ "appBuilder:createProject": ADMIN_ANY,
41886
+ "appBuilder:deleteProject": ADMIN_ANY,
41887
+ "appBuilder:updateProjectPort": ADMIN_ANY
41811
41888
  };
41812
41889
  function computeGrantForFrame(method, frame) {
41813
41890
  const rule = METHOD_GRANT_MAP[method];
@@ -41823,12 +41900,12 @@ function computeGrantForFrame(method, frame) {
41823
41900
  }
41824
41901
 
41825
41902
  // src/extension/runtime.ts
41826
- var import_node_child_process7 = require("child_process");
41903
+ var import_node_child_process8 = require("child_process");
41827
41904
  var import_node_path42 = __toESM(require("path"), 1);
41828
41905
  var import_promises10 = require("timers/promises");
41829
41906
 
41830
41907
  // src/extension/port-allocator.ts
41831
- var import_node_net = __toESM(require("net"), 1);
41908
+ var import_node_net2 = __toESM(require("net"), 1);
41832
41909
  var PortExhaustedError = class extends Error {
41833
41910
  constructor(min, max) {
41834
41911
  super(`no free port in [${min},${max}]`);
@@ -41841,7 +41918,7 @@ var PortExhaustedError = class extends Error {
41841
41918
  };
41842
41919
  function probe(port) {
41843
41920
  return new Promise((resolve6) => {
41844
- const srv = import_node_net.default.createServer();
41921
+ const srv = import_node_net2.default.createServer();
41845
41922
  srv.once("error", () => resolve6(false));
41846
41923
  srv.once("listening", () => {
41847
41924
  srv.close(() => resolve6(true));
@@ -41931,7 +42008,7 @@ var Runtime = class {
41931
42008
  CLAWOS_EXT_PORT: String(port),
41932
42009
  CLAWOS_EXT_ID: extId
41933
42010
  };
41934
- const child = (0, import_node_child_process7.spawn)("sh", ["-c", cmd], {
42011
+ const child = (0, import_node_child_process8.spawn)("sh", ["-c", cmd], {
41935
42012
  cwd: dir,
41936
42013
  env,
41937
42014
  stdio: ["ignore", "pipe", "pipe"],
@@ -42197,11 +42274,8 @@ async function startDaemon(config) {
42197
42274
  } else if (config.tunnel) {
42198
42275
  resolvedAuthToken = authFile.token;
42199
42276
  }
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);
42277
+ const ownerPrincipalId = authFile.ownerPrincipalId;
42278
+ const ownerDisplayName = loadOwnerDisplayName(config.dataDir);
42205
42279
  const authMode = resolvedAuthToken == null ? "none" : "first-message";
42206
42280
  const inboxStore = new InboxStore(config.dataDir);
42207
42281
  const inboxManager = new InboxManager(inboxStore, (_peerOwnerId, frame) => {
@@ -42243,9 +42317,7 @@ async function startDaemon(config) {
42243
42317
  isOwnerToken: (x) => resolvedAuthToken != null && constantTimeEqual(x, resolvedAuthToken),
42244
42318
  ownerPrincipalId,
42245
42319
  ownerDisplayName,
42246
- // 飞书 gate(spec 决策 #9):未激活飞书身份时不接受 guest(capability token)入站——
42247
- // 不传 registry 让 guest token 一律 BAD_TOKEN。owner 自己连接不受影响。
42248
- ...feishuActive ? { capabilityRegistry } : {},
42320
+ capabilityRegistry,
42249
42321
  selfPrincipalId,
42250
42322
  selfDisplayName
42251
42323
  });
@@ -42402,13 +42474,13 @@ async function startDaemon(config) {
42402
42474
  const publishedChannelStore = new PublishedChannelStore(publishedChannelsFile());
42403
42475
  await publishedChannelStore.load();
42404
42476
  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({
42477
+ const appBuilderStore = new ProjectStore(
42478
+ import_node_path45.default.join(config.dataDir, "personas/persona-app-builder")
42479
+ );
42480
+ const devServerSupervisor = new DevServerSupervisor();
42481
+ const deleteSessionsByProject = async (_name) => {
42482
+ };
42483
+ const handlers = buildMethodHandlers({
42412
42484
  manager,
42413
42485
  workspace,
42414
42486
  skills,
@@ -42481,10 +42553,11 @@ async function startDaemon(config) {
42481
42553
  // extension sharing v1 — owner-side published-channels store.
42482
42554
  publishedChannelStore,
42483
42555
  bundleCache,
42484
- // 飞书统一身份 Phase 1:登录 RPC handlers(auth:login:start / getIdentity / logout)
42485
- feishuAuth: { loginFlow, ownerIdentityStore }
42556
+ // app-builder
42557
+ appBuilderStore,
42558
+ deleteSessionsByProject,
42559
+ devServerLifecycle: devServerSupervisor
42486
42560
  });
42487
- let handlers = makeHandlers();
42488
42561
  const authResolver = new AuthContextResolver({
42489
42562
  ownerToken: resolvedAuthToken
42490
42563
  });
@@ -42510,29 +42583,8 @@ async function startDaemon(config) {
42510
42583
  },
42511
42584
  // POST /extensions/import lands in ~/.clawd/extensions/<id>/
42512
42585
  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
- }
42586
+ // app-builder build 模式:/preview/<port>/ 反代 dev server 的端口白名单(见 spec §三.2)
42587
+ previewPorts: config.previewPorts ?? void 0
42536
42588
  });
42537
42589
  wsServer = new LocalWsServer({
42538
42590
  host: config.host,
@@ -42571,6 +42623,8 @@ async function startDaemon(config) {
42571
42623
  },
42572
42624
  // file-sharing HTTP 路由复用 daemon 同端口(spec §5 第 3 条);router 自己处理 auth + 404
42573
42625
  httpRequestHandler: httpRouter,
42626
+ // app-builder build 模式:/preview/<port>/ HMR websocket upgrade 转发的端口白名单(同 http-router 配置)
42627
+ previewPorts: config.previewPorts ?? void 0,
42574
42628
  // 订阅成功后给该 client 重放 in-flight pendingQuestions(plan: clawd-question-server-truth)。
42575
42629
  // daemon 是 pendingQuestions 的唯一 source of truth;新 client 接入 / 刷新页面时
42576
42630
  // 把当前所有未决 question 以 session:question 帧定向回放,让 UI 不再误显示 Ended。
@@ -42615,12 +42669,6 @@ async function startDaemon(config) {
42615
42669
  const requestId = typeof frame.requestId === "string" ? frame.requestId : void 0;
42616
42670
  const handler = handlers[type];
42617
42671
  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
42672
  const ctx = wsServer.getClientContext(client.id) ?? ownerContext(ownerPrincipalId, ownerDisplayName);
42625
42673
  const verdict = computeGrantForFrame(type, frame);
42626
42674
  if (verdict.kind === "check") {
@@ -42693,6 +42741,7 @@ async function startDaemon(config) {
42693
42741
  stateSnapshot = { ...stateSnapshot, tunnelUrl: r.url, tunnelError: void 0 };
42694
42742
  stateMgr.write(stateSnapshot);
42695
42743
  currentTunnelUrl = r.url;
42744
+ wss.broadcastAll({ type: "tunnel:ready", url: r.url, subdomain: r.subdomain });
42696
42745
  const connectUrl = resolvedAuthToken ? `${r.url}#token=${resolvedAuthToken}` : r.url;
42697
42746
  const lines = [
42698
42747
  `Tunnel: ${r.url}`,