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