@clawos-dev/clawd 0.2.107 → 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 +416 -103
  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
@@ -180,7 +180,17 @@ var init_methods = __esm({
180
180
  // guest-self: guest 在自己 daemon 上做安装与更新(ADMIN_ANY,guest 是 owner of self daemon).
181
181
  // 无 subscription 持久化:guest 端身份完全来自文件系统 + reconciled publishedExtensions.
182
182
  "extension.install-from-channel",
183
- "extension.update-from-channel"
183
+ "extension.update-from-channel",
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"
184
194
  ];
185
195
  }
186
196
  });
@@ -4522,7 +4532,7 @@ var init_persona_schemas = __esm({
4522
4532
  });
4523
4533
 
4524
4534
  // ../protocol/src/schemas.ts
4525
- 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;
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;
4526
4536
  var init_schemas = __esm({
4527
4537
  "../protocol/src/schemas.ts"() {
4528
4538
  "use strict";
@@ -4619,6 +4629,10 @@ var init_schemas = __esm({
4619
4629
  // owner-mode persona session 身份标识;UI 用它在 SessionList 过滤 + jump,
4620
4630
  // daemon 用它做 idempotent dedupe + spawn 时派生 ctx.personaMode='owner'
4621
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(),
4622
4636
  // listener-mode sub-session 的原始 chatId(owner-mode 不写;listener-scope 强制写入)。
4623
4637
  // 派生 sessionId 是单向 hash `${personaId}-${tokenHash12}-${chatHash8}`,必须保留原始 chatId
4624
4638
  // 才能让 alice 重连同一 sub-session(持久化在 alice localStorage 之外,由 daemon push 同步)。
@@ -4891,7 +4905,12 @@ var init_schemas = __esm({
4891
4905
  forkedFromSessionId: external_exports.string().min(1).optional(),
4892
4906
  // owner-mode persona session:传此字段时 daemon 自行派生 cwd 和启动参数,
4893
4907
  // 且按 ownerPersonaId 做 idempotent dedupe(每 persona 永远只有一个 owner session)
4894
- 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()
4895
4914
  }).refine((args) => args.cwd != null || args.ownerPersonaId != null, {
4896
4915
  message: "cwd \u4E0E ownerPersonaId \u81F3\u5C11\u4F20\u4E00\u4E2A"
4897
4916
  });
@@ -5162,6 +5181,41 @@ var init_schemas = __esm({
5162
5181
  /** file-sharing HTTP 路由的 Authorization: Bearer token;与 WS token 解耦,HTTP 401 仅触发 ReAuth 不强踢 WS(spec §11 第 4 条) */
5163
5182
  httpToken: external_exports.string().optional()
5164
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();
5165
5219
  }
5166
5220
  });
5167
5221
 
@@ -6773,8 +6827,8 @@ var require_atomic_sleep = __commonJS({
6773
6827
  var require_sonic_boom = __commonJS({
6774
6828
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
6775
6829
  "use strict";
6776
- var fs47 = require("fs");
6777
- var EventEmitter2 = require("events");
6830
+ var fs48 = require("fs");
6831
+ var EventEmitter3 = require("events");
6778
6832
  var inherits = require("util").inherits;
6779
6833
  var path59 = require("path");
6780
6834
  var sleep = require_atomic_sleep();
@@ -6830,20 +6884,20 @@ var require_sonic_boom = __commonJS({
6830
6884
  const mode = sonic.mode;
6831
6885
  if (sonic.sync) {
6832
6886
  try {
6833
- if (sonic.mkdir) fs47.mkdirSync(path59.dirname(file), { recursive: true });
6834
- const fd = fs47.openSync(file, flags, mode);
6887
+ if (sonic.mkdir) fs48.mkdirSync(path59.dirname(file), { recursive: true });
6888
+ const fd = fs48.openSync(file, flags, mode);
6835
6889
  fileOpened(null, fd);
6836
6890
  } catch (err) {
6837
6891
  fileOpened(err);
6838
6892
  throw err;
6839
6893
  }
6840
6894
  } else if (sonic.mkdir) {
6841
- fs47.mkdir(path59.dirname(file), { recursive: true }, (err) => {
6895
+ fs48.mkdir(path59.dirname(file), { recursive: true }, (err) => {
6842
6896
  if (err) return fileOpened(err);
6843
- fs47.open(file, flags, mode, fileOpened);
6897
+ fs48.open(file, flags, mode, fileOpened);
6844
6898
  });
6845
6899
  } else {
6846
- fs47.open(file, flags, mode, fileOpened);
6900
+ fs48.open(file, flags, mode, fileOpened);
6847
6901
  }
6848
6902
  }
6849
6903
  function SonicBoom(opts) {
@@ -6884,8 +6938,8 @@ var require_sonic_boom = __commonJS({
6884
6938
  this.flush = flushBuffer;
6885
6939
  this.flushSync = flushBufferSync;
6886
6940
  this._actualWrite = actualWriteBuffer;
6887
- fsWriteSync = () => fs47.writeSync(this.fd, this._writingBuf);
6888
- fsWrite = () => fs47.write(this.fd, this._writingBuf, this.release);
6941
+ fsWriteSync = () => fs48.writeSync(this.fd, this._writingBuf);
6942
+ fsWrite = () => fs48.write(this.fd, this._writingBuf, this.release);
6889
6943
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
6890
6944
  this._writingBuf = "";
6891
6945
  this.write = write;
@@ -6894,15 +6948,15 @@ var require_sonic_boom = __commonJS({
6894
6948
  this._actualWrite = actualWrite;
6895
6949
  fsWriteSync = () => {
6896
6950
  if (Buffer.isBuffer(this._writingBuf)) {
6897
- return fs47.writeSync(this.fd, this._writingBuf);
6951
+ return fs48.writeSync(this.fd, this._writingBuf);
6898
6952
  }
6899
- return fs47.writeSync(this.fd, this._writingBuf, "utf8");
6953
+ return fs48.writeSync(this.fd, this._writingBuf, "utf8");
6900
6954
  };
6901
6955
  fsWrite = () => {
6902
6956
  if (Buffer.isBuffer(this._writingBuf)) {
6903
- return fs47.write(this.fd, this._writingBuf, this.release);
6957
+ return fs48.write(this.fd, this._writingBuf, this.release);
6904
6958
  }
6905
- return fs47.write(this.fd, this._writingBuf, "utf8", this.release);
6959
+ return fs48.write(this.fd, this._writingBuf, "utf8", this.release);
6906
6960
  };
6907
6961
  } else {
6908
6962
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -6959,7 +7013,7 @@ var require_sonic_boom = __commonJS({
6959
7013
  }
6960
7014
  }
6961
7015
  if (this._fsync) {
6962
- fs47.fsyncSync(this.fd);
7016
+ fs48.fsyncSync(this.fd);
6963
7017
  }
6964
7018
  const len = this._len;
6965
7019
  if (this._reopening) {
@@ -7011,7 +7065,7 @@ var require_sonic_boom = __commonJS({
7011
7065
  sonic._asyncDrainScheduled = false;
7012
7066
  sonic.emit("drain");
7013
7067
  }
7014
- inherits(SonicBoom, EventEmitter2);
7068
+ inherits(SonicBoom, EventEmitter3);
7015
7069
  function mergeBuf(bufs, len) {
7016
7070
  if (bufs.length === 0) {
7017
7071
  return kEmptyBuffer;
@@ -7073,7 +7127,7 @@ var require_sonic_boom = __commonJS({
7073
7127
  const onDrain = () => {
7074
7128
  if (!this._fsync) {
7075
7129
  try {
7076
- fs47.fsync(this.fd, (err) => {
7130
+ fs48.fsync(this.fd, (err) => {
7077
7131
  this._flushPending = false;
7078
7132
  cb(err);
7079
7133
  });
@@ -7175,7 +7229,7 @@ var require_sonic_boom = __commonJS({
7175
7229
  const fd = this.fd;
7176
7230
  this.once("ready", () => {
7177
7231
  if (fd !== this.fd) {
7178
- fs47.close(fd, (err) => {
7232
+ fs48.close(fd, (err) => {
7179
7233
  if (err) {
7180
7234
  return this.emit("error", err);
7181
7235
  }
@@ -7224,7 +7278,7 @@ var require_sonic_boom = __commonJS({
7224
7278
  buf = this._bufs[0];
7225
7279
  }
7226
7280
  try {
7227
- const n = Buffer.isBuffer(buf) ? fs47.writeSync(this.fd, buf) : fs47.writeSync(this.fd, buf, "utf8");
7281
+ const n = Buffer.isBuffer(buf) ? fs48.writeSync(this.fd, buf) : fs48.writeSync(this.fd, buf, "utf8");
7228
7282
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
7229
7283
  buf = releasedBufObj.writingBuf;
7230
7284
  this._len = releasedBufObj.len;
@@ -7240,7 +7294,7 @@ var require_sonic_boom = __commonJS({
7240
7294
  }
7241
7295
  }
7242
7296
  try {
7243
- fs47.fsyncSync(this.fd);
7297
+ fs48.fsyncSync(this.fd);
7244
7298
  } catch {
7245
7299
  }
7246
7300
  }
@@ -7261,7 +7315,7 @@ var require_sonic_boom = __commonJS({
7261
7315
  buf = mergeBuf(this._bufs[0], this._lens[0]);
7262
7316
  }
7263
7317
  try {
7264
- const n = fs47.writeSync(this.fd, buf);
7318
+ const n = fs48.writeSync(this.fd, buf);
7265
7319
  buf = buf.subarray(n);
7266
7320
  this._len = Math.max(this._len - n, 0);
7267
7321
  if (buf.length <= 0) {
@@ -7289,13 +7343,13 @@ var require_sonic_boom = __commonJS({
7289
7343
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
7290
7344
  if (this.sync) {
7291
7345
  try {
7292
- const written = Buffer.isBuffer(this._writingBuf) ? fs47.writeSync(this.fd, this._writingBuf) : fs47.writeSync(this.fd, this._writingBuf, "utf8");
7346
+ const written = Buffer.isBuffer(this._writingBuf) ? fs48.writeSync(this.fd, this._writingBuf) : fs48.writeSync(this.fd, this._writingBuf, "utf8");
7293
7347
  release(null, written);
7294
7348
  } catch (err) {
7295
7349
  release(err);
7296
7350
  }
7297
7351
  } else {
7298
- fs47.write(this.fd, this._writingBuf, release);
7352
+ fs48.write(this.fd, this._writingBuf, release);
7299
7353
  }
7300
7354
  }
7301
7355
  function actualWriteBuffer() {
@@ -7304,7 +7358,7 @@ var require_sonic_boom = __commonJS({
7304
7358
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
7305
7359
  if (this.sync) {
7306
7360
  try {
7307
- const written = fs47.writeSync(this.fd, this._writingBuf);
7361
+ const written = fs48.writeSync(this.fd, this._writingBuf);
7308
7362
  release(null, written);
7309
7363
  } catch (err) {
7310
7364
  release(err);
@@ -7313,7 +7367,7 @@ var require_sonic_boom = __commonJS({
7313
7367
  if (kCopyBuffer) {
7314
7368
  this._writingBuf = Buffer.from(this._writingBuf);
7315
7369
  }
7316
- fs47.write(this.fd, this._writingBuf, release);
7370
+ fs48.write(this.fd, this._writingBuf, release);
7317
7371
  }
7318
7372
  }
7319
7373
  function actualClose(sonic) {
@@ -7329,12 +7383,12 @@ var require_sonic_boom = __commonJS({
7329
7383
  sonic._lens = [];
7330
7384
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
7331
7385
  try {
7332
- fs47.fsync(sonic.fd, closeWrapped);
7386
+ fs48.fsync(sonic.fd, closeWrapped);
7333
7387
  } catch {
7334
7388
  }
7335
7389
  function closeWrapped() {
7336
7390
  if (sonic.fd !== 1 && sonic.fd !== 2) {
7337
- fs47.close(sonic.fd, done);
7391
+ fs48.close(sonic.fd, done);
7338
7392
  } else {
7339
7393
  done();
7340
7394
  }
@@ -7589,9 +7643,9 @@ var require_thread_stream = __commonJS({
7589
7643
  "../node_modules/.pnpm/thread-stream@3.1.0/node_modules/thread-stream/index.js"(exports2, module2) {
7590
7644
  "use strict";
7591
7645
  var { version: version2 } = require_package();
7592
- var { EventEmitter: EventEmitter2 } = require("events");
7646
+ var { EventEmitter: EventEmitter3 } = require("events");
7593
7647
  var { Worker } = require("worker_threads");
7594
- var { join: join11 } = require("path");
7648
+ var { join: join12 } = require("path");
7595
7649
  var { pathToFileURL } = require("url");
7596
7650
  var { wait } = require_wait();
7597
7651
  var {
@@ -7627,7 +7681,7 @@ var require_thread_stream = __commonJS({
7627
7681
  function createWorker(stream, opts) {
7628
7682
  const { filename, workerData } = opts;
7629
7683
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
7630
- const toExecute = bundlerOverrides["thread-stream-worker"] || join11(__dirname, "lib", "worker.js");
7684
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join12(__dirname, "lib", "worker.js");
7631
7685
  const worker = new Worker(toExecute, {
7632
7686
  ...opts.workerOpts,
7633
7687
  trackUnmanagedFds: false,
@@ -7745,7 +7799,7 @@ var require_thread_stream = __commonJS({
7745
7799
  stream.worker.off("exit", onWorkerExit);
7746
7800
  destroy(stream, code !== 0 ? new Error("the worker thread exited") : null);
7747
7801
  }
7748
- var ThreadStream = class extends EventEmitter2 {
7802
+ var ThreadStream = class extends EventEmitter3 {
7749
7803
  constructor(opts = {}) {
7750
7804
  super();
7751
7805
  if (opts.bufferSize < 4) {
@@ -8013,7 +8067,7 @@ var require_transport = __commonJS({
8013
8067
  "use strict";
8014
8068
  var { createRequire } = require("module");
8015
8069
  var getCallers = require_caller();
8016
- var { join: join11, isAbsolute: isAbsolute2, sep: sep3 } = require("path");
8070
+ var { join: join12, isAbsolute: isAbsolute2, sep: sep3 } = require("path");
8017
8071
  var sleep = require_atomic_sleep();
8018
8072
  var onExit = require_on_exit_leak_free();
8019
8073
  var ThreadStream = require_thread_stream();
@@ -8076,7 +8130,7 @@ var require_transport = __commonJS({
8076
8130
  throw new Error("only one of target or targets can be specified");
8077
8131
  }
8078
8132
  if (targets) {
8079
- target = bundlerOverrides["pino-worker"] || join11(__dirname, "worker.js");
8133
+ target = bundlerOverrides["pino-worker"] || join12(__dirname, "worker.js");
8080
8134
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
8081
8135
  return {
8082
8136
  ...dest,
@@ -8094,7 +8148,7 @@ var require_transport = __commonJS({
8094
8148
  });
8095
8149
  });
8096
8150
  } else if (pipeline3) {
8097
- target = bundlerOverrides["pino-worker"] || join11(__dirname, "worker.js");
8151
+ target = bundlerOverrides["pino-worker"] || join12(__dirname, "worker.js");
8098
8152
  options.pipelines = [pipeline3.map((dest) => {
8099
8153
  return {
8100
8154
  ...dest,
@@ -8116,7 +8170,7 @@ var require_transport = __commonJS({
8116
8170
  return origin;
8117
8171
  }
8118
8172
  if (origin === "pino/file") {
8119
- return join11(__dirname, "..", "file.js");
8173
+ return join12(__dirname, "..", "file.js");
8120
8174
  }
8121
8175
  let fixTarget2;
8122
8176
  for (const filePath of callers) {
@@ -8703,7 +8757,7 @@ var require_meta = __commonJS({
8703
8757
  var require_proto = __commonJS({
8704
8758
  "../node_modules/.pnpm/pino@9.14.0/node_modules/pino/lib/proto.js"(exports2, module2) {
8705
8759
  "use strict";
8706
- var { EventEmitter: EventEmitter2 } = require("events");
8760
+ var { EventEmitter: EventEmitter3 } = require("events");
8707
8761
  var {
8708
8762
  lsCacheSym,
8709
8763
  levelValSym,
@@ -8785,7 +8839,7 @@ var require_proto = __commonJS({
8785
8839
  [getLevelSym]: getLevel,
8786
8840
  [setLevelSym]: setLevel
8787
8841
  };
8788
- Object.setPrototypeOf(prototype, EventEmitter2.prototype);
8842
+ Object.setPrototypeOf(prototype, EventEmitter3.prototype);
8789
8843
  module2.exports = function() {
8790
8844
  return Object.create(prototype);
8791
8845
  };
@@ -9106,7 +9160,7 @@ var require_safe_stable_stringify = __commonJS({
9106
9160
  return circularValue;
9107
9161
  }
9108
9162
  let res = "";
9109
- let join11 = ",";
9163
+ let join12 = ",";
9110
9164
  const originalIndentation = indentation;
9111
9165
  if (Array.isArray(value)) {
9112
9166
  if (value.length === 0) {
@@ -9120,7 +9174,7 @@ var require_safe_stable_stringify = __commonJS({
9120
9174
  indentation += spacer;
9121
9175
  res += `
9122
9176
  ${indentation}`;
9123
- join11 = `,
9177
+ join12 = `,
9124
9178
  ${indentation}`;
9125
9179
  }
9126
9180
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -9128,13 +9182,13 @@ ${indentation}`;
9128
9182
  for (; i < maximumValuesToStringify - 1; i++) {
9129
9183
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
9130
9184
  res += tmp2 !== void 0 ? tmp2 : "null";
9131
- res += join11;
9185
+ res += join12;
9132
9186
  }
9133
9187
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
9134
9188
  res += tmp !== void 0 ? tmp : "null";
9135
9189
  if (value.length - 1 > maximumBreadth) {
9136
9190
  const removedKeys = value.length - maximumBreadth - 1;
9137
- res += `${join11}"... ${getItemCount(removedKeys)} not stringified"`;
9191
+ res += `${join12}"... ${getItemCount(removedKeys)} not stringified"`;
9138
9192
  }
9139
9193
  if (spacer !== "") {
9140
9194
  res += `
@@ -9155,7 +9209,7 @@ ${originalIndentation}`;
9155
9209
  let separator = "";
9156
9210
  if (spacer !== "") {
9157
9211
  indentation += spacer;
9158
- join11 = `,
9212
+ join12 = `,
9159
9213
  ${indentation}`;
9160
9214
  whitespace = " ";
9161
9215
  }
@@ -9169,13 +9223,13 @@ ${indentation}`;
9169
9223
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
9170
9224
  if (tmp !== void 0) {
9171
9225
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
9172
- separator = join11;
9226
+ separator = join12;
9173
9227
  }
9174
9228
  }
9175
9229
  if (keyLength > maximumBreadth) {
9176
9230
  const removedKeys = keyLength - maximumBreadth;
9177
9231
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
9178
- separator = join11;
9232
+ separator = join12;
9179
9233
  }
9180
9234
  if (spacer !== "" && separator.length > 1) {
9181
9235
  res = `
@@ -9216,7 +9270,7 @@ ${originalIndentation}`;
9216
9270
  }
9217
9271
  const originalIndentation = indentation;
9218
9272
  let res = "";
9219
- let join11 = ",";
9273
+ let join12 = ",";
9220
9274
  if (Array.isArray(value)) {
9221
9275
  if (value.length === 0) {
9222
9276
  return "[]";
@@ -9229,7 +9283,7 @@ ${originalIndentation}`;
9229
9283
  indentation += spacer;
9230
9284
  res += `
9231
9285
  ${indentation}`;
9232
- join11 = `,
9286
+ join12 = `,
9233
9287
  ${indentation}`;
9234
9288
  }
9235
9289
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -9237,13 +9291,13 @@ ${indentation}`;
9237
9291
  for (; i < maximumValuesToStringify - 1; i++) {
9238
9292
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
9239
9293
  res += tmp2 !== void 0 ? tmp2 : "null";
9240
- res += join11;
9294
+ res += join12;
9241
9295
  }
9242
9296
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
9243
9297
  res += tmp !== void 0 ? tmp : "null";
9244
9298
  if (value.length - 1 > maximumBreadth) {
9245
9299
  const removedKeys = value.length - maximumBreadth - 1;
9246
- res += `${join11}"... ${getItemCount(removedKeys)} not stringified"`;
9300
+ res += `${join12}"... ${getItemCount(removedKeys)} not stringified"`;
9247
9301
  }
9248
9302
  if (spacer !== "") {
9249
9303
  res += `
@@ -9256,7 +9310,7 @@ ${originalIndentation}`;
9256
9310
  let whitespace = "";
9257
9311
  if (spacer !== "") {
9258
9312
  indentation += spacer;
9259
- join11 = `,
9313
+ join12 = `,
9260
9314
  ${indentation}`;
9261
9315
  whitespace = " ";
9262
9316
  }
@@ -9265,7 +9319,7 @@ ${indentation}`;
9265
9319
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
9266
9320
  if (tmp !== void 0) {
9267
9321
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
9268
- separator = join11;
9322
+ separator = join12;
9269
9323
  }
9270
9324
  }
9271
9325
  if (spacer !== "" && separator.length > 1) {
@@ -9323,20 +9377,20 @@ ${originalIndentation}`;
9323
9377
  indentation += spacer;
9324
9378
  let res2 = `
9325
9379
  ${indentation}`;
9326
- const join12 = `,
9380
+ const join13 = `,
9327
9381
  ${indentation}`;
9328
9382
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
9329
9383
  let i = 0;
9330
9384
  for (; i < maximumValuesToStringify - 1; i++) {
9331
9385
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
9332
9386
  res2 += tmp2 !== void 0 ? tmp2 : "null";
9333
- res2 += join12;
9387
+ res2 += join13;
9334
9388
  }
9335
9389
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
9336
9390
  res2 += tmp !== void 0 ? tmp : "null";
9337
9391
  if (value.length - 1 > maximumBreadth) {
9338
9392
  const removedKeys = value.length - maximumBreadth - 1;
9339
- res2 += `${join12}"... ${getItemCount(removedKeys)} not stringified"`;
9393
+ res2 += `${join13}"... ${getItemCount(removedKeys)} not stringified"`;
9340
9394
  }
9341
9395
  res2 += `
9342
9396
  ${originalIndentation}`;
@@ -9352,16 +9406,16 @@ ${originalIndentation}`;
9352
9406
  return '"[Object]"';
9353
9407
  }
9354
9408
  indentation += spacer;
9355
- const join11 = `,
9409
+ const join12 = `,
9356
9410
  ${indentation}`;
9357
9411
  let res = "";
9358
9412
  let separator = "";
9359
9413
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
9360
9414
  if (isTypedArrayWithEntries(value)) {
9361
- res += stringifyTypedArray(value, join11, maximumBreadth);
9415
+ res += stringifyTypedArray(value, join12, maximumBreadth);
9362
9416
  keys = keys.slice(value.length);
9363
9417
  maximumPropertiesToStringify -= value.length;
9364
- separator = join11;
9418
+ separator = join12;
9365
9419
  }
9366
9420
  if (deterministic) {
9367
9421
  keys = sort(keys, comparator);
@@ -9372,13 +9426,13 @@ ${indentation}`;
9372
9426
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
9373
9427
  if (tmp !== void 0) {
9374
9428
  res += `${separator}${strEscape(key2)}: ${tmp}`;
9375
- separator = join11;
9429
+ separator = join12;
9376
9430
  }
9377
9431
  }
9378
9432
  if (keyLength > maximumBreadth) {
9379
9433
  const removedKeys = keyLength - maximumBreadth;
9380
9434
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
9381
- separator = join11;
9435
+ separator = join12;
9382
9436
  }
9383
9437
  if (separator !== "") {
9384
9438
  res = `
@@ -19260,7 +19314,7 @@ var require_extension = __commonJS({
19260
19314
  var require_websocket = __commonJS({
19261
19315
  "../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket.js"(exports2, module2) {
19262
19316
  "use strict";
19263
- var EventEmitter2 = require("events");
19317
+ var EventEmitter3 = require("events");
19264
19318
  var https = require("https");
19265
19319
  var http3 = require("http");
19266
19320
  var net3 = require("net");
@@ -19292,7 +19346,7 @@ var require_websocket = __commonJS({
19292
19346
  var protocolVersions = [8, 13];
19293
19347
  var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
19294
19348
  var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
19295
- var WebSocket2 = class _WebSocket extends EventEmitter2 {
19349
+ var WebSocket2 = class _WebSocket extends EventEmitter3 {
19296
19350
  /**
19297
19351
  * Create a new `WebSocket`.
19298
19352
  *
@@ -20289,7 +20343,7 @@ var require_subprotocol = __commonJS({
20289
20343
  var require_websocket_server = __commonJS({
20290
20344
  "../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket-server.js"(exports2, module2) {
20291
20345
  "use strict";
20292
- var EventEmitter2 = require("events");
20346
+ var EventEmitter3 = require("events");
20293
20347
  var http3 = require("http");
20294
20348
  var { Duplex } = require("stream");
20295
20349
  var { createHash: createHash2 } = require("crypto");
@@ -20302,7 +20356,7 @@ var require_websocket_server = __commonJS({
20302
20356
  var RUNNING = 0;
20303
20357
  var CLOSING = 1;
20304
20358
  var CLOSED = 2;
20305
- var WebSocketServer2 = class extends EventEmitter2 {
20359
+ var WebSocketServer2 = class extends EventEmitter3 {
20306
20360
  /**
20307
20361
  * Create a `WebSocketServer` instance.
20308
20362
  *
@@ -20965,7 +21019,7 @@ var require_BufferList = __commonJS({
20965
21019
  this.head = this.tail = null;
20966
21020
  this.length = 0;
20967
21021
  };
20968
- BufferList.prototype.join = function join11(s) {
21022
+ BufferList.prototype.join = function join12(s) {
20969
21023
  if (this.length === 0) return "";
20970
21024
  var p2 = this.head;
20971
21025
  var ret = "" + p2.data;
@@ -30422,7 +30476,7 @@ var require_lib3 = __commonJS({
30422
30476
  // src/run-case/recorder.ts
30423
30477
  function startRunCaseRecorder(opts) {
30424
30478
  const now = opts.now ?? Date.now;
30425
- const dir = import_node_path45.default.dirname(opts.recordPath);
30479
+ const dir = import_node_path46.default.dirname(opts.recordPath);
30426
30480
  let stream = null;
30427
30481
  let closing = false;
30428
30482
  let closedSettled = false;
@@ -30436,8 +30490,8 @@ function startRunCaseRecorder(opts) {
30436
30490
  });
30437
30491
  const ensureStream = () => {
30438
30492
  if (stream) return stream;
30439
- import_node_fs30.default.mkdirSync(dir, { recursive: true });
30440
- stream = import_node_fs30.default.createWriteStream(opts.recordPath, { flags: "a" });
30493
+ import_node_fs31.default.mkdirSync(dir, { recursive: true });
30494
+ stream = import_node_fs31.default.createWriteStream(opts.recordPath, { flags: "a" });
30441
30495
  stream.on("close", () => closedResolve());
30442
30496
  return stream;
30443
30497
  };
@@ -30462,12 +30516,12 @@ function startRunCaseRecorder(opts) {
30462
30516
  };
30463
30517
  return { tap, close, closed };
30464
30518
  }
30465
- var import_node_fs30, import_node_path45;
30519
+ var import_node_fs31, import_node_path46;
30466
30520
  var init_recorder = __esm({
30467
30521
  "src/run-case/recorder.ts"() {
30468
30522
  "use strict";
30469
- import_node_fs30 = __toESM(require("fs"), 1);
30470
- import_node_path45 = __toESM(require("path"), 1);
30523
+ import_node_fs31 = __toESM(require("fs"), 1);
30524
+ import_node_path46 = __toESM(require("path"), 1);
30471
30525
  }
30472
30526
  });
30473
30527
 
@@ -30510,7 +30564,7 @@ var init_wire = __esm({
30510
30564
  // src/run-case/controller.ts
30511
30565
  async function runController(opts) {
30512
30566
  const now = opts.now ?? Date.now;
30513
- const cwd = opts.cwd ?? (0, import_node_fs31.mkdtempSync)(import_node_path46.default.join(import_node_os19.default.tmpdir(), "clawd-runcase-"));
30567
+ const cwd = opts.cwd ?? (0, import_node_fs32.mkdtempSync)(import_node_path47.default.join(import_node_os19.default.tmpdir(), "clawd-runcase-"));
30514
30568
  const ownsCwd = opts.cwd === void 0;
30515
30569
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
30516
30570
  const spawnCtx = { cwd };
@@ -30671,19 +30725,19 @@ async function runController(opts) {
30671
30725
  if (sigintHandler) process.off("SIGINT", sigintHandler);
30672
30726
  if (ownsCwd) {
30673
30727
  try {
30674
- (0, import_node_fs31.rmSync)(cwd, { recursive: true, force: true });
30728
+ (0, import_node_fs32.rmSync)(cwd, { recursive: true, force: true });
30675
30729
  } catch {
30676
30730
  }
30677
30731
  }
30678
30732
  return exitCode ?? 0;
30679
30733
  }
30680
- var import_node_fs31, import_node_os19, import_node_path46;
30734
+ var import_node_fs32, import_node_os19, import_node_path47;
30681
30735
  var init_controller = __esm({
30682
30736
  "src/run-case/controller.ts"() {
30683
30737
  "use strict";
30684
- import_node_fs31 = require("fs");
30738
+ import_node_fs32 = require("fs");
30685
30739
  import_node_os19 = __toESM(require("os"), 1);
30686
- import_node_path46 = __toESM(require("path"), 1);
30740
+ import_node_path47 = __toESM(require("path"), 1);
30687
30741
  init_claude();
30688
30742
  init_stdout_splitter();
30689
30743
  init_permission_stdio();
@@ -30918,8 +30972,8 @@ Env (advanced):
30918
30972
  `;
30919
30973
 
30920
30974
  // src/index.ts
30921
- var import_node_path44 = __toESM(require("path"), 1);
30922
- var import_node_fs29 = __toESM(require("fs"), 1);
30975
+ var import_node_path45 = __toESM(require("path"), 1);
30976
+ var import_node_fs30 = __toESM(require("fs"), 1);
30923
30977
 
30924
30978
  // src/logger.ts
30925
30979
  var import_node_fs2 = __toESM(require("fs"), 1);
@@ -32928,6 +32982,7 @@ var SessionManager = class {
32928
32982
  iconKey: args.iconKey,
32929
32983
  forkedFromSessionId: args.forkedFromSessionId,
32930
32984
  ownerPersonaId: args.ownerPersonaId,
32985
+ appBuilderProject: args.appBuilderProject,
32931
32986
  creatorPrincipalId,
32932
32987
  ...creatorOwnerPrincipalId ? { creatorOwnerPrincipalId } : {},
32933
32988
  createdAt: iso,
@@ -34564,6 +34619,15 @@ var DEFAULT_PERSONAS = [
34564
34619
  model: "opus",
34565
34620
  iconKey: "assist",
34566
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
34567
34631
  }
34568
34632
  ];
34569
34633
  function findDefaultsRoot() {
@@ -41319,6 +41383,61 @@ function buildExtensionHandlers(deps) {
41319
41383
  };
41320
41384
  }
41321
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
+
41322
41441
  // src/extension/registry.ts
41323
41442
  var import_promises8 = __toESM(require("fs/promises"), 1);
41324
41443
  var import_node_path39 = __toESM(require("path"), 1);
@@ -41448,10 +41567,186 @@ function buildMethodHandlers(deps) {
41448
41567
  root: extensionsRoot(),
41449
41568
  publishedChannelStore: deps.publishedChannelStore,
41450
41569
  bundleCache: deps.bundleCache
41570
+ }),
41571
+ ...buildAppBuilderHandlers({
41572
+ store: deps.appBuilderStore,
41573
+ deleteSessionsByProject: deps.deleteSessionsByProject,
41574
+ devServerLifecycle: deps.devServerLifecycle
41451
41575
  })
41452
41576
  };
41453
41577
  }
41454
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
+
41455
41750
  // src/handlers/method-grants.ts
41456
41751
  var ADMIN_ANY = {
41457
41752
  kind: "fixed",
@@ -41581,7 +41876,15 @@ var METHOD_GRANT_MAP = {
41581
41876
  "extension.fetchBundle": CAPABILITY_SCOPED,
41582
41877
  // guest-self:guest 在自己 daemon 上触发安装与更新(无持久化 subscription 概念)
41583
41878
  "extension.install-from-channel": ADMIN_ANY,
41584
- "extension.update-from-channel": ADMIN_ANY
41879
+ "extension.update-from-channel": 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
41585
41888
  };
41586
41889
  function computeGrantForFrame(method, frame) {
41587
41890
  const rule = METHOD_GRANT_MAP[method];
@@ -41597,8 +41900,8 @@ function computeGrantForFrame(method, frame) {
41597
41900
  }
41598
41901
 
41599
41902
  // src/extension/runtime.ts
41600
- var import_node_child_process7 = require("child_process");
41601
- var import_node_path41 = __toESM(require("path"), 1);
41903
+ var import_node_child_process8 = require("child_process");
41904
+ var import_node_path42 = __toESM(require("path"), 1);
41602
41905
  var import_promises10 = require("timers/promises");
41603
41906
 
41604
41907
  // src/extension/port-allocator.ts
@@ -41699,13 +42002,13 @@ var Runtime = class {
41699
42002
  /\$CLAWOS_EXT_PORT/g,
41700
42003
  String(port)
41701
42004
  );
41702
- const dir = import_node_path41.default.join(this.root, extId);
42005
+ const dir = import_node_path42.default.join(this.root, extId);
41703
42006
  const env = {
41704
42007
  ...process.env,
41705
42008
  CLAWOS_EXT_PORT: String(port),
41706
42009
  CLAWOS_EXT_ID: extId
41707
42010
  };
41708
- const child = (0, import_node_child_process7.spawn)("sh", ["-c", cmd], {
42011
+ const child = (0, import_node_child_process8.spawn)("sh", ["-c", cmd], {
41709
42012
  cwd: dir,
41710
42013
  env,
41711
42014
  stdio: ["ignore", "pipe", "pipe"],
@@ -41811,7 +42114,7 @@ ${handle.stderrTail}`
41811
42114
 
41812
42115
  // src/extension/published-channels.ts
41813
42116
  var import_promises11 = __toESM(require("fs/promises"), 1);
41814
- var import_node_path42 = __toESM(require("path"), 1);
42117
+ var import_node_path43 = __toESM(require("path"), 1);
41815
42118
  init_zod();
41816
42119
  var PublishedChannelsError = class extends Error {
41817
42120
  constructor(code, message) {
@@ -41910,7 +42213,7 @@ var PublishedChannelStore = class {
41910
42213
  )
41911
42214
  };
41912
42215
  const tmp = `${this.filePath}.tmp`;
41913
- await import_promises11.default.mkdir(import_node_path42.default.dirname(this.filePath), { recursive: true });
42216
+ await import_promises11.default.mkdir(import_node_path43.default.dirname(this.filePath), { recursive: true });
41914
42217
  await import_promises11.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
41915
42218
  await import_promises11.default.rename(tmp, this.filePath);
41916
42219
  }
@@ -41918,7 +42221,7 @@ var PublishedChannelStore = class {
41918
42221
 
41919
42222
  // src/extension/bundle-cache.ts
41920
42223
  var import_promises12 = __toESM(require("fs/promises"), 1);
41921
- var import_node_path43 = __toESM(require("path"), 1);
42224
+ var import_node_path44 = __toESM(require("path"), 1);
41922
42225
  var BundleCache = class {
41923
42226
  constructor(rootDir) {
41924
42227
  this.rootDir = rootDir;
@@ -41927,14 +42230,14 @@ var BundleCache = class {
41927
42230
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
41928
42231
  async write(snapshotHash, buffer) {
41929
42232
  await import_promises12.default.mkdir(this.rootDir, { recursive: true });
41930
- const file = import_node_path43.default.join(this.rootDir, `${snapshotHash}.zip`);
42233
+ const file = import_node_path44.default.join(this.rootDir, `${snapshotHash}.zip`);
41931
42234
  const tmp = `${file}.tmp`;
41932
42235
  await import_promises12.default.writeFile(tmp, buffer, { mode: 384 });
41933
42236
  await import_promises12.default.rename(tmp, file);
41934
42237
  }
41935
42238
  /** Returns the bundle bytes, or null when the file doesn't exist. */
41936
42239
  async read(snapshotHash) {
41937
- const file = import_node_path43.default.join(this.rootDir, `${snapshotHash}.zip`);
42240
+ const file = import_node_path44.default.join(this.rootDir, `${snapshotHash}.zip`);
41938
42241
  try {
41939
42242
  return await import_promises12.default.readFile(file);
41940
42243
  } catch (e) {
@@ -41944,7 +42247,7 @@ var BundleCache = class {
41944
42247
  }
41945
42248
  /** Idempotent — missing file is not an error. */
41946
42249
  async delete(snapshotHash) {
41947
- const file = import_node_path43.default.join(this.rootDir, `${snapshotHash}.zip`);
42250
+ const file = import_node_path44.default.join(this.rootDir, `${snapshotHash}.zip`);
41948
42251
  await import_promises12.default.rm(file, { force: true });
41949
42252
  }
41950
42253
  };
@@ -41953,7 +42256,7 @@ var BundleCache = class {
41953
42256
  async function startDaemon(config) {
41954
42257
  const logger = createLogger({
41955
42258
  level: config.logLevel,
41956
- file: import_node_path44.default.join(config.dataDir, "clawd.log")
42259
+ file: import_node_path45.default.join(config.dataDir, "clawd.log")
41957
42260
  });
41958
42261
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
41959
42262
  const stateMgr = new StateFileManager({ dataDir: config.dataDir });
@@ -42040,7 +42343,7 @@ async function startDaemon(config) {
42040
42343
  const agents = new AgentsScanner();
42041
42344
  const history = new ClaudeHistoryReader();
42042
42345
  let transport = null;
42043
- const personaStore = new PersonaStore(import_node_path44.default.join(config.dataDir, "personas"));
42346
+ const personaStore = new PersonaStore(import_node_path45.default.join(config.dataDir, "personas"));
42044
42347
  const defaultsRoot = findDefaultsRoot();
42045
42348
  if (defaultsRoot) {
42046
42349
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -42057,7 +42360,7 @@ async function startDaemon(config) {
42057
42360
  getAdapter,
42058
42361
  historyReader: history,
42059
42362
  dataDir: config.dataDir,
42060
- personaRoot: import_node_path44.default.join(config.dataDir, "personas"),
42363
+ personaRoot: import_node_path45.default.join(config.dataDir, "personas"),
42061
42364
  personaStore,
42062
42365
  ownerDisplayName,
42063
42366
  ownerPrincipalId,
@@ -42081,10 +42384,10 @@ async function startDaemon(config) {
42081
42384
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
42082
42385
  attachmentGroup: {
42083
42386
  onFileEdit: (input) => {
42084
- const absPath = import_node_path44.default.isAbsolute(input.relPath) ? input.relPath : import_node_path44.default.join(input.cwd, input.relPath);
42387
+ const absPath = import_node_path45.default.isAbsolute(input.relPath) ? input.relPath : import_node_path45.default.join(input.cwd, input.relPath);
42085
42388
  let size = 0;
42086
42389
  try {
42087
- size = import_node_fs29.default.statSync(absPath).size;
42390
+ size = import_node_fs30.default.statSync(absPath).size;
42088
42391
  } catch (err) {
42089
42392
  logger.warn("attachment.onFileEdit stat failed", {
42090
42393
  sessionId: input.sessionId,
@@ -42171,6 +42474,12 @@ async function startDaemon(config) {
42171
42474
  const publishedChannelStore = new PublishedChannelStore(publishedChannelsFile());
42172
42475
  await publishedChannelStore.load();
42173
42476
  const bundleCache = new BundleCache(bundleCacheRoot());
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
+ };
42174
42483
  const handlers = buildMethodHandlers({
42175
42484
  manager,
42176
42485
  workspace,
@@ -42210,10 +42519,10 @@ async function startDaemon(config) {
42210
42519
  // 'persona/<pid>/owner',default 走 'default'。
42211
42520
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
42212
42521
  // guest path guard:candidate 必须在 personaRoot 子树下
42213
- personaRoot: import_node_path44.default.join(config.dataDir, "personas")
42522
+ personaRoot: import_node_path45.default.join(config.dataDir, "personas")
42214
42523
  },
42215
42524
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
42216
- personaRoot: import_node_path44.default.join(config.dataDir, "personas"),
42525
+ personaRoot: import_node_path45.default.join(config.dataDir, "personas"),
42217
42526
  // capability:list / delete handler 依赖
42218
42527
  capabilityManager,
42219
42528
  // personal-cap:get 返回的 shareBaseUrl 用此 base URL:
@@ -42243,7 +42552,11 @@ async function startDaemon(config) {
42243
42552
  extensionRuntime,
42244
42553
  // extension sharing v1 — owner-side published-channels store.
42245
42554
  publishedChannelStore,
42246
- bundleCache
42555
+ bundleCache,
42556
+ // app-builder
42557
+ appBuilderStore,
42558
+ deleteSessionsByProject,
42559
+ devServerLifecycle: devServerSupervisor
42247
42560
  });
42248
42561
  const authResolver = new AuthContextResolver({
42249
42562
  ownerToken: resolvedAuthToken
@@ -42433,8 +42746,8 @@ async function startDaemon(config) {
42433
42746
  const lines = [
42434
42747
  `Tunnel: ${r.url}`,
42435
42748
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
42436
- `Frpc config: ${import_node_path44.default.join(config.dataDir, "frpc.toml")}`,
42437
- `Frpc log: ${import_node_path44.default.join(config.dataDir, "frpc.log")}`
42749
+ `Frpc config: ${import_node_path45.default.join(config.dataDir, "frpc.toml")}`,
42750
+ `Frpc log: ${import_node_path45.default.join(config.dataDir, "frpc.log")}`
42438
42751
  ];
42439
42752
  const width = Math.max(...lines.map((l) => l.length));
42440
42753
  const bar = "\u2550".repeat(width + 4);
@@ -42447,8 +42760,8 @@ ${bar}
42447
42760
 
42448
42761
  `);
42449
42762
  try {
42450
- const connectPath = import_node_path44.default.join(config.dataDir, "connect.txt");
42451
- import_node_fs29.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
42763
+ const connectPath = import_node_path45.default.join(config.dataDir, "connect.txt");
42764
+ import_node_fs30.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
42452
42765
  } catch {
42453
42766
  }
42454
42767
  } catch (err) {
@@ -42514,9 +42827,9 @@ ${bar}
42514
42827
  };
42515
42828
  }
42516
42829
  function migrateDropPersonsDir(dataDir) {
42517
- const dir = import_node_path44.default.join(dataDir, "persons");
42830
+ const dir = import_node_path45.default.join(dataDir, "persons");
42518
42831
  try {
42519
- import_node_fs29.default.rmSync(dir, { recursive: true, force: true });
42832
+ import_node_fs30.default.rmSync(dir, { recursive: true, force: true });
42520
42833
  } catch {
42521
42834
  }
42522
42835
  }