@clawos-dev/clawd 0.2.123-beta.243.5a9518b → 0.2.123-beta.244.5e5107f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +736 -97
- package/dist/persona-defaults/persona-app-builder/CLAUDE.md +12 -1
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/main.ts +17 -2
- package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/new-extension.sh +1 -1
- package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/publish.sh +18 -2
- package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/verify.sh +20 -2
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -207,7 +207,16 @@ var init_methods = __esm({
|
|
|
207
207
|
"appBuilder:reportStage",
|
|
208
208
|
// 显式控制 fix (spec 2026-06-02-app-builder-explicit-dev-server-control):用户点"收起预览"按钮触发,
|
|
209
209
|
// 对称 startDevServer。砍掉了 setActiveSession(dev server 不再跟用户 focus 走),切 session 不动 dev server。
|
|
210
|
-
"appBuilder:stopDevServer"
|
|
210
|
+
"appBuilder:stopDevServer",
|
|
211
|
+
// 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design):
|
|
212
|
+
// publish:PreviewPane「发布上线」按钮 onClick 调本 RPC,daemon 内串行化 + spawn publish.sh +
|
|
213
|
+
// 广播阶段进度帧 (appBuilder:publish-progress / publish-failed)。同 project 已有 in-flight job
|
|
214
|
+
// 返回 already-publishing + 既有 jobId(UI 不报错继续订阅)。失败时 daemon 调 manager.send()
|
|
215
|
+
// 注入 user_text 到 chat,由 assistant 接管完成部署。
|
|
216
|
+
// dismissPublishJob:取消 in-flight publish(SIGTERM 子进程 + 清字段)或清掉 daemon 崩溃留下的
|
|
217
|
+
// 中断残骸 publishJob 字段。UI 端 [取消] / [interrupted 取消] 都走这个 RPC。
|
|
218
|
+
"appBuilder:publish",
|
|
219
|
+
"appBuilder:dismissPublishJob"
|
|
211
220
|
];
|
|
212
221
|
}
|
|
213
222
|
});
|
|
@@ -4557,7 +4566,7 @@ var init_persona_schemas = __esm({
|
|
|
4557
4566
|
});
|
|
4558
4567
|
|
|
4559
4568
|
// ../protocol/src/schemas.ts
|
|
4560
|
-
var SessionStatusSchema, UsageSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, ParsedEventSchema, SessionCreateArgs, SessionIdArgs, SessionUpdateArgs, SessionSendArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsArgs, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionSubscribeArgs, SessionPinArgs, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, HistoryRecentDirsArgs, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, AuthRequestFrameSchema, AuthOkFrameSchema, TunnelReadyEventSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema, PROJECT_PORT_MIN, PROJECT_PORT_MAX, projectNameRegex, DEFAULT_DEV_COMMAND, ProjectMetadataSchema, PROJECT_STAGE_VALUES, ProjectStageSchema, ProjectWithStatusSchema, ListProjectsArgsSchema, ListProjectsResultSchema, CreateProjectArgsSchema, CreateProjectResultSchema, DeleteProjectArgsSchema, DeleteProjectResultSchema, UpdateProjectPortArgsSchema, UpdateProjectPortResultSchema, SetProdUrlArgsSchema, SetProdUrlResultSchema, StartDevServerArgsSchema, StartDevServerResultSchema, ASSISTANT_REPORTABLE_STAGES, ReportStageArgsSchema, ReportStageResultSchema, StopDevServerArgsSchema, StopDevServerResultSchema, AppBuilderProjectUpdatedEventSchema;
|
|
4569
|
+
var SessionStatusSchema, UsageSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, ParsedEventSchema, SessionCreateArgs, SessionIdArgs, SessionUpdateArgs, SessionSendArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsArgs, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionSubscribeArgs, SessionPinArgs, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, HistoryRecentDirsArgs, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, AuthRequestFrameSchema, AuthOkFrameSchema, TunnelReadyEventSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema, PROJECT_PORT_MIN, PROJECT_PORT_MAX, projectNameRegex, DEFAULT_DEV_COMMAND, ProjectMetadataSchema, PROJECT_STAGE_VALUES, ProjectStageSchema, ProjectWithStatusSchema, ListProjectsArgsSchema, ListProjectsResultSchema, CreateProjectArgsSchema, CreateProjectResultSchema, DeleteProjectArgsSchema, DeleteProjectResultSchema, UpdateProjectPortArgsSchema, UpdateProjectPortResultSchema, SetProdUrlArgsSchema, SetProdUrlResultSchema, StartDevServerArgsSchema, StartDevServerResultSchema, ASSISTANT_REPORTABLE_STAGES, ReportStageArgsSchema, ReportStageResultSchema, StopDevServerArgsSchema, StopDevServerResultSchema, AppBuilderProjectUpdatedEventSchema, PublishArgsSchema, PublishResultSchema, DismissPublishJobArgsSchema, DismissPublishJobResultSchema, AppBuilderPublishProgressEventSchema, AppBuilderPublishFailedEventSchema;
|
|
4561
4570
|
var init_schemas = __esm({
|
|
4562
4571
|
"../protocol/src/schemas.ts"() {
|
|
4563
4572
|
"use strict";
|
|
@@ -5218,7 +5227,22 @@ var init_schemas = __esm({
|
|
|
5218
5227
|
* optional:缺省时 supervisor 用默认 /Local:\s+https?:\/\//(vite "Local: http://..." 输出)。
|
|
5219
5228
|
* 老板换非 vite 技术栈(Next.js "Ready in"、webpack "Compiled" 等)时自定义这个字段。
|
|
5220
5229
|
*/
|
|
5221
|
-
readyPattern: external_exports.string().optional()
|
|
5230
|
+
readyPattern: external_exports.string().optional(),
|
|
5231
|
+
/**
|
|
5232
|
+
* 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.1.4):
|
|
5233
|
+
* in-flight 发布 job 的元数据。仅承载"in-flight"或"中断残骸"两种状态:
|
|
5234
|
+
* - daemon spawn publish.sh + 收到第一个 ::stage::build marker 时写入
|
|
5235
|
+
* - 每次 stage 切换更新 stage 字段
|
|
5236
|
+
* - 成功 / 正常失败 / 用户 dismiss 时立即清掉
|
|
5237
|
+
* - 只有 daemon 崩才会留残骸(子进程随 daemon 一起死)
|
|
5238
|
+
* status 字段在 ProjectWithStatus 派生而不在这里 —— 本 schema 是落盘形态,
|
|
5239
|
+
* "in-flight vs interrupted" 区分由 daemon 响应时实时拼 registry 状态得出。
|
|
5240
|
+
*/
|
|
5241
|
+
publishJob: external_exports.object({
|
|
5242
|
+
jobId: external_exports.string().min(1),
|
|
5243
|
+
stage: external_exports.enum(["build", "deploy", "verify"]),
|
|
5244
|
+
startedAt: external_exports.number().int().nonnegative()
|
|
5245
|
+
}).optional()
|
|
5222
5246
|
});
|
|
5223
5247
|
PROJECT_STAGE_VALUES = [
|
|
5224
5248
|
"install-pending",
|
|
@@ -5245,7 +5269,21 @@ var init_schemas = __esm({
|
|
|
5245
5269
|
*/
|
|
5246
5270
|
stage: ProjectStageSchema.optional(),
|
|
5247
5271
|
/** stage='failed' 时携带失败原因 */
|
|
5248
|
-
stageReason: external_exports.string().optional()
|
|
5272
|
+
stageReason: external_exports.string().optional(),
|
|
5273
|
+
/**
|
|
5274
|
+
* 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.1.3):
|
|
5275
|
+
* ProjectMetadata.publishJob 的 status 派生版。daemon 响应时按以下规则实时拼装:
|
|
5276
|
+
* - 落盘字段存在 + PublishJobRegistry 有匹配 jobId → 'in-flight'
|
|
5277
|
+
* - 落盘字段存在 + Registry 无匹配(jobId 对不上或 entry 缺失)→ 'interrupted'(daemon 崩残骸)
|
|
5278
|
+
* - 落盘字段不存在 → undefined(字段省略)
|
|
5279
|
+
* UI 用 status 决定 PreviewPane 渲染分支(in-flight 三段 checklist / interrupted 恢复条)。
|
|
5280
|
+
*/
|
|
5281
|
+
publishJob: external_exports.object({
|
|
5282
|
+
jobId: external_exports.string().min(1),
|
|
5283
|
+
stage: external_exports.enum(["build", "deploy", "verify"]),
|
|
5284
|
+
startedAt: external_exports.number().int().nonnegative(),
|
|
5285
|
+
status: external_exports.enum(["in-flight", "interrupted"])
|
|
5286
|
+
}).optional()
|
|
5249
5287
|
});
|
|
5250
5288
|
ListProjectsArgsSchema = external_exports.object({}).strict();
|
|
5251
5289
|
ListProjectsResultSchema = external_exports.object({
|
|
@@ -5310,6 +5348,33 @@ var init_schemas = __esm({
|
|
|
5310
5348
|
/** stage='failed' 时的失败原因 */
|
|
5311
5349
|
stageReason: external_exports.string().optional()
|
|
5312
5350
|
}).strict();
|
|
5351
|
+
PublishArgsSchema = external_exports.object({
|
|
5352
|
+
name: external_exports.string().regex(projectNameRegex)
|
|
5353
|
+
});
|
|
5354
|
+
PublishResultSchema = external_exports.object({
|
|
5355
|
+
jobId: external_exports.string().min(1),
|
|
5356
|
+
status: external_exports.enum(["started", "already-publishing"])
|
|
5357
|
+
}).strict();
|
|
5358
|
+
DismissPublishJobArgsSchema = external_exports.object({
|
|
5359
|
+
name: external_exports.string().regex(projectNameRegex)
|
|
5360
|
+
});
|
|
5361
|
+
DismissPublishJobResultSchema = external_exports.object({
|
|
5362
|
+
ok: external_exports.literal(true)
|
|
5363
|
+
}).strict();
|
|
5364
|
+
AppBuilderPublishProgressEventSchema = external_exports.object({
|
|
5365
|
+
type: external_exports.literal("appBuilder:publish-progress"),
|
|
5366
|
+
name: external_exports.string().regex(projectNameRegex),
|
|
5367
|
+
jobId: external_exports.string().min(1),
|
|
5368
|
+
stage: external_exports.enum(["build", "deploy", "verify"]),
|
|
5369
|
+
status: external_exports.enum(["started", "completed"])
|
|
5370
|
+
}).strict();
|
|
5371
|
+
AppBuilderPublishFailedEventSchema = external_exports.object({
|
|
5372
|
+
type: external_exports.literal("appBuilder:publish-failed"),
|
|
5373
|
+
name: external_exports.string().regex(projectNameRegex),
|
|
5374
|
+
jobId: external_exports.string().min(1),
|
|
5375
|
+
stage: external_exports.enum(["build", "deploy", "verify", "unknown"]),
|
|
5376
|
+
errorSummary: external_exports.string()
|
|
5377
|
+
}).strict();
|
|
5313
5378
|
}
|
|
5314
5379
|
});
|
|
5315
5380
|
|
|
@@ -7835,7 +7900,7 @@ var require_thread_stream = __commonJS({
|
|
|
7835
7900
|
var { version: version2 } = require_package();
|
|
7836
7901
|
var { EventEmitter: EventEmitter3 } = require("events");
|
|
7837
7902
|
var { Worker } = require("worker_threads");
|
|
7838
|
-
var { join:
|
|
7903
|
+
var { join: join15 } = require("path");
|
|
7839
7904
|
var { pathToFileURL } = require("url");
|
|
7840
7905
|
var { wait } = require_wait();
|
|
7841
7906
|
var {
|
|
@@ -7871,7 +7936,7 @@ var require_thread_stream = __commonJS({
|
|
|
7871
7936
|
function createWorker(stream, opts) {
|
|
7872
7937
|
const { filename, workerData } = opts;
|
|
7873
7938
|
const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
|
|
7874
|
-
const toExecute = bundlerOverrides["thread-stream-worker"] ||
|
|
7939
|
+
const toExecute = bundlerOverrides["thread-stream-worker"] || join15(__dirname, "lib", "worker.js");
|
|
7875
7940
|
const worker = new Worker(toExecute, {
|
|
7876
7941
|
...opts.workerOpts,
|
|
7877
7942
|
trackUnmanagedFds: false,
|
|
@@ -8257,7 +8322,7 @@ var require_transport = __commonJS({
|
|
|
8257
8322
|
"use strict";
|
|
8258
8323
|
var { createRequire } = require("module");
|
|
8259
8324
|
var getCallers = require_caller();
|
|
8260
|
-
var { join:
|
|
8325
|
+
var { join: join15, isAbsolute: isAbsolute2, sep: sep3 } = require("path");
|
|
8261
8326
|
var sleep = require_atomic_sleep();
|
|
8262
8327
|
var onExit = require_on_exit_leak_free();
|
|
8263
8328
|
var ThreadStream = require_thread_stream();
|
|
@@ -8320,7 +8385,7 @@ var require_transport = __commonJS({
|
|
|
8320
8385
|
throw new Error("only one of target or targets can be specified");
|
|
8321
8386
|
}
|
|
8322
8387
|
if (targets) {
|
|
8323
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
8388
|
+
target = bundlerOverrides["pino-worker"] || join15(__dirname, "worker.js");
|
|
8324
8389
|
options.targets = targets.filter((dest) => dest.target).map((dest) => {
|
|
8325
8390
|
return {
|
|
8326
8391
|
...dest,
|
|
@@ -8338,7 +8403,7 @@ var require_transport = __commonJS({
|
|
|
8338
8403
|
});
|
|
8339
8404
|
});
|
|
8340
8405
|
} else if (pipeline3) {
|
|
8341
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
8406
|
+
target = bundlerOverrides["pino-worker"] || join15(__dirname, "worker.js");
|
|
8342
8407
|
options.pipelines = [pipeline3.map((dest) => {
|
|
8343
8408
|
return {
|
|
8344
8409
|
...dest,
|
|
@@ -8360,7 +8425,7 @@ var require_transport = __commonJS({
|
|
|
8360
8425
|
return origin;
|
|
8361
8426
|
}
|
|
8362
8427
|
if (origin === "pino/file") {
|
|
8363
|
-
return
|
|
8428
|
+
return join15(__dirname, "..", "file.js");
|
|
8364
8429
|
}
|
|
8365
8430
|
let fixTarget2;
|
|
8366
8431
|
for (const filePath of callers) {
|
|
@@ -9350,7 +9415,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
9350
9415
|
return circularValue;
|
|
9351
9416
|
}
|
|
9352
9417
|
let res = "";
|
|
9353
|
-
let
|
|
9418
|
+
let join15 = ",";
|
|
9354
9419
|
const originalIndentation = indentation;
|
|
9355
9420
|
if (Array.isArray(value)) {
|
|
9356
9421
|
if (value.length === 0) {
|
|
@@ -9364,7 +9429,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
9364
9429
|
indentation += spacer;
|
|
9365
9430
|
res += `
|
|
9366
9431
|
${indentation}`;
|
|
9367
|
-
|
|
9432
|
+
join15 = `,
|
|
9368
9433
|
${indentation}`;
|
|
9369
9434
|
}
|
|
9370
9435
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -9372,13 +9437,13 @@ ${indentation}`;
|
|
|
9372
9437
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
9373
9438
|
const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
9374
9439
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
9375
|
-
res +=
|
|
9440
|
+
res += join15;
|
|
9376
9441
|
}
|
|
9377
9442
|
const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
9378
9443
|
res += tmp !== void 0 ? tmp : "null";
|
|
9379
9444
|
if (value.length - 1 > maximumBreadth) {
|
|
9380
9445
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
9381
|
-
res += `${
|
|
9446
|
+
res += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
9382
9447
|
}
|
|
9383
9448
|
if (spacer !== "") {
|
|
9384
9449
|
res += `
|
|
@@ -9399,7 +9464,7 @@ ${originalIndentation}`;
|
|
|
9399
9464
|
let separator = "";
|
|
9400
9465
|
if (spacer !== "") {
|
|
9401
9466
|
indentation += spacer;
|
|
9402
|
-
|
|
9467
|
+
join15 = `,
|
|
9403
9468
|
${indentation}`;
|
|
9404
9469
|
whitespace = " ";
|
|
9405
9470
|
}
|
|
@@ -9413,13 +9478,13 @@ ${indentation}`;
|
|
|
9413
9478
|
const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
|
|
9414
9479
|
if (tmp !== void 0) {
|
|
9415
9480
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
9416
|
-
separator =
|
|
9481
|
+
separator = join15;
|
|
9417
9482
|
}
|
|
9418
9483
|
}
|
|
9419
9484
|
if (keyLength > maximumBreadth) {
|
|
9420
9485
|
const removedKeys = keyLength - maximumBreadth;
|
|
9421
9486
|
res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
|
|
9422
|
-
separator =
|
|
9487
|
+
separator = join15;
|
|
9423
9488
|
}
|
|
9424
9489
|
if (spacer !== "" && separator.length > 1) {
|
|
9425
9490
|
res = `
|
|
@@ -9460,7 +9525,7 @@ ${originalIndentation}`;
|
|
|
9460
9525
|
}
|
|
9461
9526
|
const originalIndentation = indentation;
|
|
9462
9527
|
let res = "";
|
|
9463
|
-
let
|
|
9528
|
+
let join15 = ",";
|
|
9464
9529
|
if (Array.isArray(value)) {
|
|
9465
9530
|
if (value.length === 0) {
|
|
9466
9531
|
return "[]";
|
|
@@ -9473,7 +9538,7 @@ ${originalIndentation}`;
|
|
|
9473
9538
|
indentation += spacer;
|
|
9474
9539
|
res += `
|
|
9475
9540
|
${indentation}`;
|
|
9476
|
-
|
|
9541
|
+
join15 = `,
|
|
9477
9542
|
${indentation}`;
|
|
9478
9543
|
}
|
|
9479
9544
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -9481,13 +9546,13 @@ ${indentation}`;
|
|
|
9481
9546
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
9482
9547
|
const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
9483
9548
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
9484
|
-
res +=
|
|
9549
|
+
res += join15;
|
|
9485
9550
|
}
|
|
9486
9551
|
const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
9487
9552
|
res += tmp !== void 0 ? tmp : "null";
|
|
9488
9553
|
if (value.length - 1 > maximumBreadth) {
|
|
9489
9554
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
9490
|
-
res += `${
|
|
9555
|
+
res += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
9491
9556
|
}
|
|
9492
9557
|
if (spacer !== "") {
|
|
9493
9558
|
res += `
|
|
@@ -9500,7 +9565,7 @@ ${originalIndentation}`;
|
|
|
9500
9565
|
let whitespace = "";
|
|
9501
9566
|
if (spacer !== "") {
|
|
9502
9567
|
indentation += spacer;
|
|
9503
|
-
|
|
9568
|
+
join15 = `,
|
|
9504
9569
|
${indentation}`;
|
|
9505
9570
|
whitespace = " ";
|
|
9506
9571
|
}
|
|
@@ -9509,7 +9574,7 @@ ${indentation}`;
|
|
|
9509
9574
|
const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
|
|
9510
9575
|
if (tmp !== void 0) {
|
|
9511
9576
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
9512
|
-
separator =
|
|
9577
|
+
separator = join15;
|
|
9513
9578
|
}
|
|
9514
9579
|
}
|
|
9515
9580
|
if (spacer !== "" && separator.length > 1) {
|
|
@@ -9567,20 +9632,20 @@ ${originalIndentation}`;
|
|
|
9567
9632
|
indentation += spacer;
|
|
9568
9633
|
let res2 = `
|
|
9569
9634
|
${indentation}`;
|
|
9570
|
-
const
|
|
9635
|
+
const join16 = `,
|
|
9571
9636
|
${indentation}`;
|
|
9572
9637
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
9573
9638
|
let i = 0;
|
|
9574
9639
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
9575
9640
|
const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
9576
9641
|
res2 += tmp2 !== void 0 ? tmp2 : "null";
|
|
9577
|
-
res2 +=
|
|
9642
|
+
res2 += join16;
|
|
9578
9643
|
}
|
|
9579
9644
|
const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
9580
9645
|
res2 += tmp !== void 0 ? tmp : "null";
|
|
9581
9646
|
if (value.length - 1 > maximumBreadth) {
|
|
9582
9647
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
9583
|
-
res2 += `${
|
|
9648
|
+
res2 += `${join16}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
9584
9649
|
}
|
|
9585
9650
|
res2 += `
|
|
9586
9651
|
${originalIndentation}`;
|
|
@@ -9596,16 +9661,16 @@ ${originalIndentation}`;
|
|
|
9596
9661
|
return '"[Object]"';
|
|
9597
9662
|
}
|
|
9598
9663
|
indentation += spacer;
|
|
9599
|
-
const
|
|
9664
|
+
const join15 = `,
|
|
9600
9665
|
${indentation}`;
|
|
9601
9666
|
let res = "";
|
|
9602
9667
|
let separator = "";
|
|
9603
9668
|
let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
|
|
9604
9669
|
if (isTypedArrayWithEntries(value)) {
|
|
9605
|
-
res += stringifyTypedArray(value,
|
|
9670
|
+
res += stringifyTypedArray(value, join15, maximumBreadth);
|
|
9606
9671
|
keys = keys.slice(value.length);
|
|
9607
9672
|
maximumPropertiesToStringify -= value.length;
|
|
9608
|
-
separator =
|
|
9673
|
+
separator = join15;
|
|
9609
9674
|
}
|
|
9610
9675
|
if (deterministic) {
|
|
9611
9676
|
keys = sort(keys, comparator);
|
|
@@ -9616,13 +9681,13 @@ ${indentation}`;
|
|
|
9616
9681
|
const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
|
|
9617
9682
|
if (tmp !== void 0) {
|
|
9618
9683
|
res += `${separator}${strEscape(key2)}: ${tmp}`;
|
|
9619
|
-
separator =
|
|
9684
|
+
separator = join15;
|
|
9620
9685
|
}
|
|
9621
9686
|
}
|
|
9622
9687
|
if (keyLength > maximumBreadth) {
|
|
9623
9688
|
const removedKeys = keyLength - maximumBreadth;
|
|
9624
9689
|
res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
|
|
9625
|
-
separator =
|
|
9690
|
+
separator = join15;
|
|
9626
9691
|
}
|
|
9627
9692
|
if (separator !== "") {
|
|
9628
9693
|
res = `
|
|
@@ -21209,7 +21274,7 @@ var require_BufferList = __commonJS({
|
|
|
21209
21274
|
this.head = this.tail = null;
|
|
21210
21275
|
this.length = 0;
|
|
21211
21276
|
};
|
|
21212
|
-
BufferList.prototype.join = function
|
|
21277
|
+
BufferList.prototype.join = function join15(s) {
|
|
21213
21278
|
if (this.length === 0) return "";
|
|
21214
21279
|
var p2 = this.head;
|
|
21215
21280
|
var ret = "" + p2.data;
|
|
@@ -30666,7 +30731,7 @@ var require_lib3 = __commonJS({
|
|
|
30666
30731
|
// src/run-case/recorder.ts
|
|
30667
30732
|
function startRunCaseRecorder(opts) {
|
|
30668
30733
|
const now = opts.now ?? Date.now;
|
|
30669
|
-
const dir =
|
|
30734
|
+
const dir = import_node_path48.default.dirname(opts.recordPath);
|
|
30670
30735
|
let stream = null;
|
|
30671
30736
|
let closing = false;
|
|
30672
30737
|
let closedSettled = false;
|
|
@@ -30680,8 +30745,8 @@ function startRunCaseRecorder(opts) {
|
|
|
30680
30745
|
});
|
|
30681
30746
|
const ensureStream = () => {
|
|
30682
30747
|
if (stream) return stream;
|
|
30683
|
-
|
|
30684
|
-
stream =
|
|
30748
|
+
import_node_fs32.default.mkdirSync(dir, { recursive: true });
|
|
30749
|
+
stream = import_node_fs32.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
30685
30750
|
stream.on("close", () => closedResolve());
|
|
30686
30751
|
return stream;
|
|
30687
30752
|
};
|
|
@@ -30706,12 +30771,12 @@ function startRunCaseRecorder(opts) {
|
|
|
30706
30771
|
};
|
|
30707
30772
|
return { tap, close, closed };
|
|
30708
30773
|
}
|
|
30709
|
-
var
|
|
30774
|
+
var import_node_fs32, import_node_path48;
|
|
30710
30775
|
var init_recorder = __esm({
|
|
30711
30776
|
"src/run-case/recorder.ts"() {
|
|
30712
30777
|
"use strict";
|
|
30713
|
-
|
|
30714
|
-
|
|
30778
|
+
import_node_fs32 = __toESM(require("fs"), 1);
|
|
30779
|
+
import_node_path48 = __toESM(require("path"), 1);
|
|
30715
30780
|
}
|
|
30716
30781
|
});
|
|
30717
30782
|
|
|
@@ -30754,7 +30819,7 @@ var init_wire = __esm({
|
|
|
30754
30819
|
// src/run-case/controller.ts
|
|
30755
30820
|
async function runController(opts) {
|
|
30756
30821
|
const now = opts.now ?? Date.now;
|
|
30757
|
-
const cwd = opts.cwd ?? (0,
|
|
30822
|
+
const cwd = opts.cwd ?? (0, import_node_fs33.mkdtempSync)(import_node_path49.default.join(import_node_os19.default.tmpdir(), "clawd-runcase-"));
|
|
30758
30823
|
const ownsCwd = opts.cwd === void 0;
|
|
30759
30824
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
30760
30825
|
const spawnCtx = { cwd };
|
|
@@ -30915,19 +30980,19 @@ async function runController(opts) {
|
|
|
30915
30980
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
30916
30981
|
if (ownsCwd) {
|
|
30917
30982
|
try {
|
|
30918
|
-
(0,
|
|
30983
|
+
(0, import_node_fs33.rmSync)(cwd, { recursive: true, force: true });
|
|
30919
30984
|
} catch {
|
|
30920
30985
|
}
|
|
30921
30986
|
}
|
|
30922
30987
|
return exitCode ?? 0;
|
|
30923
30988
|
}
|
|
30924
|
-
var
|
|
30989
|
+
var import_node_fs33, import_node_os19, import_node_path49;
|
|
30925
30990
|
var init_controller = __esm({
|
|
30926
30991
|
"src/run-case/controller.ts"() {
|
|
30927
30992
|
"use strict";
|
|
30928
|
-
|
|
30993
|
+
import_node_fs33 = require("fs");
|
|
30929
30994
|
import_node_os19 = __toESM(require("os"), 1);
|
|
30930
|
-
|
|
30995
|
+
import_node_path49 = __toESM(require("path"), 1);
|
|
30931
30996
|
init_claude();
|
|
30932
30997
|
init_stdout_splitter();
|
|
30933
30998
|
init_permission_stdio();
|
|
@@ -31162,8 +31227,8 @@ Env (advanced):
|
|
|
31162
31227
|
`;
|
|
31163
31228
|
|
|
31164
31229
|
// src/index.ts
|
|
31165
|
-
var
|
|
31166
|
-
var
|
|
31230
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
31231
|
+
var import_node_fs31 = __toESM(require("fs"), 1);
|
|
31167
31232
|
|
|
31168
31233
|
// src/logger.ts
|
|
31169
31234
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
@@ -32970,6 +33035,14 @@ var SessionManager = class {
|
|
|
32970
33035
|
// guest sessions 由 handlers/session.ts list 处按 ctx.principal.kind === 'guest' 做权限过滤
|
|
32971
33036
|
// (creatorPrincipalId === capId), 这里仅负责数据源汇总.
|
|
32972
33037
|
// listener sub-session 不走这条(listener 走 persona:listSubSessions RPC 单独入口)。
|
|
33038
|
+
/**
|
|
33039
|
+
* 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.2.2):
|
|
33040
|
+
* 反查绑定到指定 app-builder project 的 session。按 invariant "1 Session ↔ 1 Project"
|
|
33041
|
+
* 应当唯一;找不到 → undefined(项目已成孤儿)。
|
|
33042
|
+
*/
|
|
33043
|
+
findOwnedSessionByAppBuilderProject(projectName) {
|
|
33044
|
+
return this.listAllOwned().find((file) => file.appBuilderProject === projectName);
|
|
33045
|
+
}
|
|
32973
33046
|
listAllOwned() {
|
|
32974
33047
|
const out = [];
|
|
32975
33048
|
out.push(...this.deps.store.list());
|
|
@@ -34941,6 +35014,34 @@ function copyBundleExtras(srcDir, dstDir) {
|
|
|
34941
35014
|
}
|
|
34942
35015
|
}
|
|
34943
35016
|
}
|
|
35017
|
+
var DAEMON_MANAGED_DIRS = ["extension-kit"];
|
|
35018
|
+
function refreshDaemonManagedDirs(args) {
|
|
35019
|
+
const entries = args.entries ?? DEFAULT_PERSONAS;
|
|
35020
|
+
for (const entry of entries) {
|
|
35021
|
+
const bundleDir = path11.join(args.defaultsRoot, entry.personaId);
|
|
35022
|
+
if (!fs9.existsSync(bundleDir)) continue;
|
|
35023
|
+
const personaDir = args.store.personaDirPath(entry.personaId);
|
|
35024
|
+
if (!fs9.existsSync(personaDir)) continue;
|
|
35025
|
+
for (const subdir of DAEMON_MANAGED_DIRS) {
|
|
35026
|
+
const srcPath = path11.join(bundleDir, subdir);
|
|
35027
|
+
if (!fs9.existsSync(srcPath)) continue;
|
|
35028
|
+
const dstPath = path11.join(personaDir, subdir);
|
|
35029
|
+
try {
|
|
35030
|
+
fs9.cpSync(srcPath, dstPath, { recursive: true, force: true, dereference: true });
|
|
35031
|
+
args.logger.info("persona.refresh.synced", {
|
|
35032
|
+
personaId: entry.personaId,
|
|
35033
|
+
subdir
|
|
35034
|
+
});
|
|
35035
|
+
} catch (err) {
|
|
35036
|
+
args.logger.warn("persona.refresh.failed", {
|
|
35037
|
+
personaId: entry.personaId,
|
|
35038
|
+
subdir,
|
|
35039
|
+
error: err instanceof Error ? err.message : String(err)
|
|
35040
|
+
});
|
|
35041
|
+
}
|
|
35042
|
+
}
|
|
35043
|
+
}
|
|
35044
|
+
}
|
|
34944
35045
|
|
|
34945
35046
|
// src/index.ts
|
|
34946
35047
|
init_claude();
|
|
@@ -42156,12 +42257,373 @@ function listPidsOnPort(port) {
|
|
|
42156
42257
|
});
|
|
42157
42258
|
}
|
|
42158
42259
|
|
|
42260
|
+
// src/app-builder/publish-registry.ts
|
|
42261
|
+
var import_node_crypto14 = require("crypto");
|
|
42262
|
+
var PublishJobRegistry = class {
|
|
42263
|
+
jobs = /* @__PURE__ */ new Map();
|
|
42264
|
+
has(name) {
|
|
42265
|
+
return this.jobs.has(name);
|
|
42266
|
+
}
|
|
42267
|
+
get(name) {
|
|
42268
|
+
return this.jobs.get(name);
|
|
42269
|
+
}
|
|
42270
|
+
/**
|
|
42271
|
+
* args.jobId 仅用于测试注入;生产代码不传,registry 自己生成。
|
|
42272
|
+
* args.now 仅用于测试注入;生产代码不传,用 Date.now()。
|
|
42273
|
+
*/
|
|
42274
|
+
create(args) {
|
|
42275
|
+
if (this.jobs.has(args.name)) {
|
|
42276
|
+
throw new Error(`already publishing: ${args.name}`);
|
|
42277
|
+
}
|
|
42278
|
+
const jobId = args.jobId ?? `job-${(0, import_node_crypto14.randomUUID)()}`;
|
|
42279
|
+
this.jobs.set(args.name, {
|
|
42280
|
+
jobId,
|
|
42281
|
+
name: args.name,
|
|
42282
|
+
stage: "build",
|
|
42283
|
+
sessionId: args.sessionId,
|
|
42284
|
+
childProc: args.childProc,
|
|
42285
|
+
dismissed: false,
|
|
42286
|
+
sawAnyStage: false,
|
|
42287
|
+
startedAt: args.now ?? Date.now()
|
|
42288
|
+
});
|
|
42289
|
+
return jobId;
|
|
42290
|
+
}
|
|
42291
|
+
updateStage(name, stage) {
|
|
42292
|
+
const job = this.jobs.get(name);
|
|
42293
|
+
if (!job) return;
|
|
42294
|
+
job.stage = stage;
|
|
42295
|
+
job.sawAnyStage = true;
|
|
42296
|
+
}
|
|
42297
|
+
markDismissed(name) {
|
|
42298
|
+
const job = this.jobs.get(name);
|
|
42299
|
+
if (!job) return;
|
|
42300
|
+
job.dismissed = true;
|
|
42301
|
+
}
|
|
42302
|
+
remove(name) {
|
|
42303
|
+
const job = this.jobs.get(name);
|
|
42304
|
+
if (!job) return void 0;
|
|
42305
|
+
this.jobs.delete(name);
|
|
42306
|
+
return { sessionId: job.sessionId };
|
|
42307
|
+
}
|
|
42308
|
+
};
|
|
42309
|
+
|
|
42310
|
+
// src/app-builder/publish-job-runner.ts
|
|
42311
|
+
var import_node_child_process8 = require("child_process");
|
|
42312
|
+
var import_node_fs29 = require("fs");
|
|
42313
|
+
var import_node_path39 = require("path");
|
|
42314
|
+
|
|
42315
|
+
// src/app-builder/publish-stage-parser.ts
|
|
42316
|
+
var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
|
|
42317
|
+
var PROD_URL_RE = /^\s*::prod-url::(\S+)\s*$/;
|
|
42318
|
+
var URL_RE = /\bhttps?:\/\/[^\s]+/;
|
|
42319
|
+
function parsePublishLine(line) {
|
|
42320
|
+
const stageMatch = STAGE_RE.exec(line);
|
|
42321
|
+
if (stageMatch) return { kind: "stage", stage: stageMatch[1] };
|
|
42322
|
+
const urlMatch = PROD_URL_RE.exec(line);
|
|
42323
|
+
if (urlMatch) return { kind: "prod-url", url: urlMatch[1] };
|
|
42324
|
+
return null;
|
|
42325
|
+
}
|
|
42326
|
+
function extractProdUrl(text) {
|
|
42327
|
+
const m2 = URL_RE.exec(text);
|
|
42328
|
+
return m2 ? m2[0] : null;
|
|
42329
|
+
}
|
|
42330
|
+
function tailStderrLines(buf, n) {
|
|
42331
|
+
if (!buf) return "";
|
|
42332
|
+
const lines = buf.split("\n");
|
|
42333
|
+
if (lines.length <= n) return buf;
|
|
42334
|
+
return lines.slice(lines.length - n).join("\n");
|
|
42335
|
+
}
|
|
42336
|
+
|
|
42337
|
+
// src/app-builder/publish-job-runner.ts
|
|
42338
|
+
async function startPublishJob(deps, args) {
|
|
42339
|
+
const { registry: registry2, projectDir } = deps;
|
|
42340
|
+
const spawn9 = deps.spawnImpl ?? import_node_child_process8.spawn;
|
|
42341
|
+
if (registry2.has(args.name)) {
|
|
42342
|
+
return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
|
|
42343
|
+
}
|
|
42344
|
+
const projDir = projectDir(args.name);
|
|
42345
|
+
const logPath = (0, import_node_path39.join)(projDir, ".publish.log");
|
|
42346
|
+
let logStream = null;
|
|
42347
|
+
try {
|
|
42348
|
+
logStream = (0, import_node_fs29.createWriteStream)(logPath, { flags: "w" });
|
|
42349
|
+
} catch {
|
|
42350
|
+
logStream = null;
|
|
42351
|
+
}
|
|
42352
|
+
const child = spawn9("bash", [args.scriptPath, projDir], {
|
|
42353
|
+
cwd: projDir,
|
|
42354
|
+
env: process.env,
|
|
42355
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
42356
|
+
});
|
|
42357
|
+
const jobId = registry2.create({
|
|
42358
|
+
name: args.name,
|
|
42359
|
+
sessionId: args.sessionId,
|
|
42360
|
+
childProc: child
|
|
42361
|
+
});
|
|
42362
|
+
deps.logger?.info("app-builder.publish.spawned", {
|
|
42363
|
+
name: args.name,
|
|
42364
|
+
jobId,
|
|
42365
|
+
pid: child.pid ?? null,
|
|
42366
|
+
scriptPath: args.scriptPath,
|
|
42367
|
+
logPath
|
|
42368
|
+
});
|
|
42369
|
+
child.on("error", (err) => {
|
|
42370
|
+
deps.logger?.warn("app-builder.publish.spawn-error", {
|
|
42371
|
+
name: args.name,
|
|
42372
|
+
jobId,
|
|
42373
|
+
error: err instanceof Error ? err.message : String(err)
|
|
42374
|
+
});
|
|
42375
|
+
void handleExit(deps, {
|
|
42376
|
+
name: args.name,
|
|
42377
|
+
jobId,
|
|
42378
|
+
code: null,
|
|
42379
|
+
signal: null,
|
|
42380
|
+
urlCandidate: null,
|
|
42381
|
+
stderrTail: err instanceof Error ? err.message : String(err),
|
|
42382
|
+
logPath
|
|
42383
|
+
});
|
|
42384
|
+
});
|
|
42385
|
+
let urlFromMarker = null;
|
|
42386
|
+
let urlFallback = null;
|
|
42387
|
+
let stdoutBuf = "";
|
|
42388
|
+
child.stdout?.on("data", (chunk) => {
|
|
42389
|
+
if (logStream) logStream.write(chunk);
|
|
42390
|
+
stdoutBuf += chunk.toString();
|
|
42391
|
+
let nlIdx;
|
|
42392
|
+
while ((nlIdx = stdoutBuf.indexOf("\n")) >= 0) {
|
|
42393
|
+
const line = stdoutBuf.slice(0, nlIdx);
|
|
42394
|
+
stdoutBuf = stdoutBuf.slice(nlIdx + 1);
|
|
42395
|
+
handleStdoutLine(deps, args.name, jobId, line);
|
|
42396
|
+
const parsedForUrl = parsePublishLine(line);
|
|
42397
|
+
if (parsedForUrl?.kind === "prod-url") urlFromMarker = parsedForUrl.url;
|
|
42398
|
+
const url = extractProdUrl(line);
|
|
42399
|
+
if (url && !urlFallback) urlFallback = url;
|
|
42400
|
+
}
|
|
42401
|
+
});
|
|
42402
|
+
let stderrBuf = "";
|
|
42403
|
+
child.stderr?.on("data", (chunk) => {
|
|
42404
|
+
if (logStream) logStream.write(chunk);
|
|
42405
|
+
stderrBuf += chunk.toString();
|
|
42406
|
+
});
|
|
42407
|
+
child.on("exit", (code, signal) => {
|
|
42408
|
+
if (logStream) logStream.end();
|
|
42409
|
+
const urlCandidate = urlFromMarker ?? urlFallback;
|
|
42410
|
+
deps.logger?.info("app-builder.publish.child-exit", {
|
|
42411
|
+
name: args.name,
|
|
42412
|
+
jobId,
|
|
42413
|
+
code,
|
|
42414
|
+
signal,
|
|
42415
|
+
hadUrl: !!urlCandidate,
|
|
42416
|
+
urlSource: urlFromMarker ? "marker" : urlFallback ? "fallback" : "none",
|
|
42417
|
+
stderrBytes: stderrBuf.length
|
|
42418
|
+
});
|
|
42419
|
+
void handleExit(deps, {
|
|
42420
|
+
name: args.name,
|
|
42421
|
+
jobId,
|
|
42422
|
+
code,
|
|
42423
|
+
signal,
|
|
42424
|
+
urlCandidate,
|
|
42425
|
+
stderrTail: tailStderrLines(stderrBuf, 20),
|
|
42426
|
+
logPath
|
|
42427
|
+
});
|
|
42428
|
+
});
|
|
42429
|
+
return { jobId, status: "started" };
|
|
42430
|
+
}
|
|
42431
|
+
function handleStdoutLine(deps, name, jobId, line) {
|
|
42432
|
+
const parsed = parsePublishLine(line);
|
|
42433
|
+
if (parsed?.kind !== "stage") return;
|
|
42434
|
+
advanceStage(deps, name, jobId, parsed.stage);
|
|
42435
|
+
}
|
|
42436
|
+
function advanceStage(deps, name, jobId, next) {
|
|
42437
|
+
const job = deps.registry.get(name);
|
|
42438
|
+
if (!job) return;
|
|
42439
|
+
const prev = job.stage;
|
|
42440
|
+
const wasFirstStage = !job.sawAnyStage;
|
|
42441
|
+
deps.logger?.info("app-builder.publish.stage-advance", {
|
|
42442
|
+
name,
|
|
42443
|
+
jobId,
|
|
42444
|
+
prev: wasFirstStage ? null : prev,
|
|
42445
|
+
next,
|
|
42446
|
+
firstStage: wasFirstStage
|
|
42447
|
+
});
|
|
42448
|
+
if (wasFirstStage) {
|
|
42449
|
+
deps.registry.updateStage(name, next);
|
|
42450
|
+
void deps.setPublishJob(name, {
|
|
42451
|
+
jobId,
|
|
42452
|
+
stage: next,
|
|
42453
|
+
startedAt: job.startedAt
|
|
42454
|
+
});
|
|
42455
|
+
deps.broadcast({
|
|
42456
|
+
type: "appBuilder:publish-progress",
|
|
42457
|
+
name,
|
|
42458
|
+
jobId,
|
|
42459
|
+
stage: next,
|
|
42460
|
+
status: "started"
|
|
42461
|
+
});
|
|
42462
|
+
return;
|
|
42463
|
+
}
|
|
42464
|
+
if (prev === next) {
|
|
42465
|
+
return;
|
|
42466
|
+
}
|
|
42467
|
+
deps.broadcast({
|
|
42468
|
+
type: "appBuilder:publish-progress",
|
|
42469
|
+
name,
|
|
42470
|
+
jobId,
|
|
42471
|
+
stage: prev,
|
|
42472
|
+
status: "completed"
|
|
42473
|
+
});
|
|
42474
|
+
deps.registry.updateStage(name, next);
|
|
42475
|
+
void deps.setPublishJob(name, {
|
|
42476
|
+
jobId,
|
|
42477
|
+
stage: next,
|
|
42478
|
+
startedAt: job.startedAt
|
|
42479
|
+
});
|
|
42480
|
+
deps.broadcast({
|
|
42481
|
+
type: "appBuilder:publish-progress",
|
|
42482
|
+
name,
|
|
42483
|
+
jobId,
|
|
42484
|
+
stage: next,
|
|
42485
|
+
status: "started"
|
|
42486
|
+
});
|
|
42487
|
+
}
|
|
42488
|
+
async function handleExit(deps, args) {
|
|
42489
|
+
const job = deps.registry.get(args.name);
|
|
42490
|
+
if (args.signal === "SIGTERM" && job?.dismissed) {
|
|
42491
|
+
try {
|
|
42492
|
+
await deps.clearPublishJob(args.name);
|
|
42493
|
+
} catch (err) {
|
|
42494
|
+
console.error("[publish] clearPublishJob failed (dismissed branch)", err);
|
|
42495
|
+
}
|
|
42496
|
+
deps.registry.remove(args.name);
|
|
42497
|
+
return;
|
|
42498
|
+
}
|
|
42499
|
+
if (args.code === 0 && args.urlCandidate) {
|
|
42500
|
+
if (job?.sawAnyStage) {
|
|
42501
|
+
deps.broadcast({
|
|
42502
|
+
type: "appBuilder:publish-progress",
|
|
42503
|
+
name: args.name,
|
|
42504
|
+
jobId: args.jobId,
|
|
42505
|
+
stage: job.stage,
|
|
42506
|
+
status: "completed"
|
|
42507
|
+
});
|
|
42508
|
+
}
|
|
42509
|
+
try {
|
|
42510
|
+
await deps.setProdUrl(args.name, args.urlCandidate);
|
|
42511
|
+
} catch (err) {
|
|
42512
|
+
console.error("[publish] setProdUrl failed; falling through to failure branch", err);
|
|
42513
|
+
await emitFailure(deps, args, "\u8C03 setProdUrl \u5931\u8D25\uFF1A" + (err instanceof Error ? err.message : String(err)));
|
|
42514
|
+
return;
|
|
42515
|
+
}
|
|
42516
|
+
try {
|
|
42517
|
+
await deps.clearPublishJob(args.name);
|
|
42518
|
+
} catch (err) {
|
|
42519
|
+
console.error("[publish] clearPublishJob failed (success branch)", err);
|
|
42520
|
+
}
|
|
42521
|
+
deps.registry.remove(args.name);
|
|
42522
|
+
return;
|
|
42523
|
+
}
|
|
42524
|
+
await emitFailure(deps, args, buildErrorSummary(args));
|
|
42525
|
+
}
|
|
42526
|
+
async function emitFailure(deps, args, errorSummary) {
|
|
42527
|
+
const job = deps.registry.get(args.name);
|
|
42528
|
+
const failStage = job?.sawAnyStage ? job.stage : "unknown";
|
|
42529
|
+
deps.logger?.warn("app-builder.publish.failed", {
|
|
42530
|
+
name: args.name,
|
|
42531
|
+
jobId: args.jobId,
|
|
42532
|
+
stage: failStage,
|
|
42533
|
+
errorSummary: errorSummary.slice(0, 500)
|
|
42534
|
+
});
|
|
42535
|
+
try {
|
|
42536
|
+
await deps.clearPublishJob(args.name);
|
|
42537
|
+
} catch (err) {
|
|
42538
|
+
deps.logger?.warn("app-builder.publish.clear-field-failed", {
|
|
42539
|
+
name: args.name,
|
|
42540
|
+
jobId: args.jobId,
|
|
42541
|
+
error: err instanceof Error ? err.message : String(err)
|
|
42542
|
+
});
|
|
42543
|
+
}
|
|
42544
|
+
const removed = deps.registry.remove(args.name);
|
|
42545
|
+
deps.broadcast({
|
|
42546
|
+
type: "appBuilder:publish-failed",
|
|
42547
|
+
name: args.name,
|
|
42548
|
+
jobId: args.jobId,
|
|
42549
|
+
stage: failStage,
|
|
42550
|
+
errorSummary
|
|
42551
|
+
});
|
|
42552
|
+
if (removed) {
|
|
42553
|
+
const msg = formatFailureMessage(failStage, errorSummary, args.logPath);
|
|
42554
|
+
try {
|
|
42555
|
+
await deps.injectUserText(removed.sessionId, msg);
|
|
42556
|
+
deps.logger?.info("app-builder.publish.failure-injected-to-chat", {
|
|
42557
|
+
name: args.name,
|
|
42558
|
+
jobId: args.jobId,
|
|
42559
|
+
sessionId: removed.sessionId
|
|
42560
|
+
});
|
|
42561
|
+
} catch (err) {
|
|
42562
|
+
deps.logger?.warn("app-builder.publish.inject-user-text-failed", {
|
|
42563
|
+
name: args.name,
|
|
42564
|
+
jobId: args.jobId,
|
|
42565
|
+
sessionId: removed.sessionId,
|
|
42566
|
+
error: err instanceof Error ? err.message : String(err)
|
|
42567
|
+
});
|
|
42568
|
+
}
|
|
42569
|
+
} else {
|
|
42570
|
+
deps.logger?.warn("app-builder.publish.no-registry-entry-on-failure", {
|
|
42571
|
+
name: args.name,
|
|
42572
|
+
jobId: args.jobId
|
|
42573
|
+
});
|
|
42574
|
+
}
|
|
42575
|
+
}
|
|
42576
|
+
function buildErrorSummary(args) {
|
|
42577
|
+
if (args.signal) {
|
|
42578
|
+
return `publish.sh \u88AB\u4E2D\u65AD\uFF08signal: ${args.signal}\uFF09`;
|
|
42579
|
+
}
|
|
42580
|
+
if (args.code === 0 && !args.urlCandidate) {
|
|
42581
|
+
return "publish.sh \u6210\u529F\u9000\u51FA\u4F46\u672A\u8F93\u51FA\u516C\u7F51 URL";
|
|
42582
|
+
}
|
|
42583
|
+
return args.stderrTail || `publish.sh \u5F02\u5E38\u9000\u51FA\uFF08exit code: ${args.code}\uFF09`;
|
|
42584
|
+
}
|
|
42585
|
+
function formatFailureMessage(stage, errorSummary, logPath) {
|
|
42586
|
+
return `\u53D1\u5E03\u5931\u8D25\uFF1A[${stage}] \u9636\u6BB5
|
|
42587
|
+
${errorSummary}
|
|
42588
|
+
|
|
42589
|
+
\u5B8C\u6574\u65E5\u5FD7\uFF1A${logPath}
|
|
42590
|
+
\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`;
|
|
42591
|
+
}
|
|
42592
|
+
async function recoverInterruptedJobs(deps) {
|
|
42593
|
+
const projects = await deps.listAllProjects();
|
|
42594
|
+
for (const p2 of projects) {
|
|
42595
|
+
if (p2.publishJob) {
|
|
42596
|
+
deps.log(
|
|
42597
|
+
`[publish] found interrupted job ${p2.publishJob.jobId} for ${p2.name}, stage=${p2.publishJob.stage}`
|
|
42598
|
+
);
|
|
42599
|
+
}
|
|
42600
|
+
}
|
|
42601
|
+
}
|
|
42602
|
+
|
|
42159
42603
|
// src/handlers/app-builder.ts
|
|
42160
42604
|
init_protocol();
|
|
42605
|
+
var import_node_path40 = require("path");
|
|
42606
|
+
var PUBLISH_SCRIPT_REL = "extension-kit/scripts/publish.sh";
|
|
42161
42607
|
var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
|
|
42608
|
+
async function recoverInterruptedPublishJobs(store, logger) {
|
|
42609
|
+
await recoverInterruptedJobs({
|
|
42610
|
+
listAllProjects: async () => store.list(),
|
|
42611
|
+
log: (msg) => logger?.info(msg)
|
|
42612
|
+
});
|
|
42613
|
+
}
|
|
42162
42614
|
function buildAppBuilderHandlers(deps) {
|
|
42163
42615
|
const { store, devServerLifecycle, sessionManager, getTunnelUrl, broadcastProjectUpdated } = deps;
|
|
42164
42616
|
const stages = /* @__PURE__ */ new Map();
|
|
42617
|
+
const publishRegistry = new PublishJobRegistry();
|
|
42618
|
+
function withPublishJobStatus(meta) {
|
|
42619
|
+
if (!meta.publishJob) return void 0;
|
|
42620
|
+
const job = publishRegistry.get(meta.name);
|
|
42621
|
+
const inFlight = !!job && job.jobId === meta.publishJob.jobId;
|
|
42622
|
+
return {
|
|
42623
|
+
...meta.publishJob,
|
|
42624
|
+
status: inFlight ? "in-flight" : "interrupted"
|
|
42625
|
+
};
|
|
42626
|
+
}
|
|
42165
42627
|
function getStageOf(name) {
|
|
42166
42628
|
const recorded = stages.get(name);
|
|
42167
42629
|
if (recorded) return recorded;
|
|
@@ -42180,6 +42642,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
42180
42642
|
return;
|
|
42181
42643
|
}
|
|
42182
42644
|
deps.logger?.info("app-builder.stage-transition", { name, stage, reason });
|
|
42645
|
+
const publishJobWithStatus = withPublishJobStatus(meta);
|
|
42183
42646
|
broadcastProjectUpdated({
|
|
42184
42647
|
type: "appBuilder:project-updated",
|
|
42185
42648
|
project: {
|
|
@@ -42187,7 +42650,8 @@ function buildAppBuilderHandlers(deps) {
|
|
|
42187
42650
|
isRunning: devServerLifecycle.isRunning(name),
|
|
42188
42651
|
boundSessionId: devServerLifecycle.boundSessionId(name),
|
|
42189
42652
|
stage,
|
|
42190
|
-
...reason !== void 0 ? { stageReason: reason } : {}
|
|
42653
|
+
...reason !== void 0 ? { stageReason: reason } : {},
|
|
42654
|
+
...publishJobWithStatus !== void 0 ? { publishJob: publishJobWithStatus } : { publishJob: void 0 }
|
|
42191
42655
|
},
|
|
42192
42656
|
stage,
|
|
42193
42657
|
...reason !== void 0 ? { stageReason: reason } : {}
|
|
@@ -42199,12 +42663,16 @@ function buildAppBuilderHandlers(deps) {
|
|
|
42199
42663
|
response: {
|
|
42200
42664
|
projects: projects.map((p2) => {
|
|
42201
42665
|
const s = getStageOf(p2.name);
|
|
42666
|
+
const publishJobWithStatus = withPublishJobStatus(p2);
|
|
42202
42667
|
return {
|
|
42203
42668
|
...p2,
|
|
42204
42669
|
isRunning: devServerLifecycle.isRunning(p2.name),
|
|
42205
42670
|
boundSessionId: devServerLifecycle.boundSessionId(p2.name),
|
|
42206
42671
|
stage: s.stage,
|
|
42207
|
-
...s.reason !== void 0 ? { stageReason: s.reason } : {}
|
|
42672
|
+
...s.reason !== void 0 ? { stageReason: s.reason } : {},
|
|
42673
|
+
// publishJob 在 spread ...p 之后覆写:原始 ProjectMetadata.publishJob 没有 status,
|
|
42674
|
+
// 这里实时拼上 status 字段;字段不存在时显式 undefined(覆盖掉 spread 的可能值)。
|
|
42675
|
+
publishJob: publishJobWithStatus
|
|
42208
42676
|
};
|
|
42209
42677
|
})
|
|
42210
42678
|
}
|
|
@@ -42423,6 +42891,103 @@ function buildAppBuilderHandlers(deps) {
|
|
|
42423
42891
|
await setStageAndBroadcast(projectName, "stopped");
|
|
42424
42892
|
return { response: { ok: true } };
|
|
42425
42893
|
};
|
|
42894
|
+
const publish = async (frame) => {
|
|
42895
|
+
const args = PublishArgsSchema.parse(frame);
|
|
42896
|
+
const projects = await store.list();
|
|
42897
|
+
const project = projects.find((p2) => p2.name === args.name);
|
|
42898
|
+
if (!project) {
|
|
42899
|
+
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `publish: project "${args.name}" not found`);
|
|
42900
|
+
}
|
|
42901
|
+
const boundSession = sessionManager.findOwnedSessionByAppBuilderProject(args.name);
|
|
42902
|
+
if (!boundSession) {
|
|
42903
|
+
throw new ClawdError(
|
|
42904
|
+
ERROR_CODES.VALIDATION_ERROR,
|
|
42905
|
+
`publish: project "${args.name}" has no bound session (orphan); cannot inject failure to chat`
|
|
42906
|
+
);
|
|
42907
|
+
}
|
|
42908
|
+
if (project.publishJob && !publishRegistry.has(args.name)) {
|
|
42909
|
+
deps.logger?.info("app-builder.publish.clear-interrupted", {
|
|
42910
|
+
name: args.name,
|
|
42911
|
+
previousJobId: project.publishJob.jobId,
|
|
42912
|
+
previousStage: project.publishJob.stage
|
|
42913
|
+
});
|
|
42914
|
+
await store.clearPublishJob(args.name);
|
|
42915
|
+
}
|
|
42916
|
+
const scriptPath = (0, import_node_path40.join)(deps.personaRoot, PUBLISH_SCRIPT_REL);
|
|
42917
|
+
deps.logger?.info("app-builder.publish.start", {
|
|
42918
|
+
name: args.name,
|
|
42919
|
+
sessionId: boundSession.sessionId,
|
|
42920
|
+
scriptPath
|
|
42921
|
+
});
|
|
42922
|
+
const result = await startPublishJob(
|
|
42923
|
+
{
|
|
42924
|
+
registry: publishRegistry,
|
|
42925
|
+
setPublishJob: async (name, job) => {
|
|
42926
|
+
await store.setPublishJob(name, job);
|
|
42927
|
+
const current = getStageOf(name);
|
|
42928
|
+
await setStageAndBroadcast(name, current.stage, current.reason);
|
|
42929
|
+
},
|
|
42930
|
+
clearPublishJob: async (name) => {
|
|
42931
|
+
await store.clearPublishJob(name);
|
|
42932
|
+
const current = getStageOf(name);
|
|
42933
|
+
await setStageAndBroadcast(name, current.stage, current.reason);
|
|
42934
|
+
},
|
|
42935
|
+
setProdUrl: async (name, url) => {
|
|
42936
|
+
await store.setProdUrl(name, url);
|
|
42937
|
+
const current = getStageOf(name);
|
|
42938
|
+
await setStageAndBroadcast(name, current.stage, current.reason);
|
|
42939
|
+
},
|
|
42940
|
+
broadcast: (f) => {
|
|
42941
|
+
broadcastProjectUpdated(f);
|
|
42942
|
+
},
|
|
42943
|
+
injectUserText: async (sessionId, text) => {
|
|
42944
|
+
const { broadcast } = sessionManager.send({ sessionId, text });
|
|
42945
|
+
for (const entry of broadcast) {
|
|
42946
|
+
deps.broadcastSessionFrame(entry.frame, entry.target);
|
|
42947
|
+
}
|
|
42948
|
+
},
|
|
42949
|
+
projectDir: (name) => store.projectDir(name),
|
|
42950
|
+
logger: deps.logger
|
|
42951
|
+
},
|
|
42952
|
+
{
|
|
42953
|
+
name: args.name,
|
|
42954
|
+
sessionId: boundSession.sessionId,
|
|
42955
|
+
scriptPath
|
|
42956
|
+
}
|
|
42957
|
+
);
|
|
42958
|
+
return { response: { ...result } };
|
|
42959
|
+
};
|
|
42960
|
+
const dismissPublishJob = async (frame) => {
|
|
42961
|
+
const args = DismissPublishJobArgsSchema.parse(frame);
|
|
42962
|
+
const projects = await store.list();
|
|
42963
|
+
const project = projects.find((p2) => p2.name === args.name);
|
|
42964
|
+
if (!project) {
|
|
42965
|
+
throw new ClawdError(
|
|
42966
|
+
ERROR_CODES.VALIDATION_ERROR,
|
|
42967
|
+
`dismissPublishJob: project "${args.name}" not found`
|
|
42968
|
+
);
|
|
42969
|
+
}
|
|
42970
|
+
const job = publishRegistry.get(args.name);
|
|
42971
|
+
if (job) {
|
|
42972
|
+
deps.logger?.info("app-builder.publish.dismiss.in-flight", {
|
|
42973
|
+
name: args.name,
|
|
42974
|
+
jobId: job.jobId,
|
|
42975
|
+
stage: job.stage
|
|
42976
|
+
});
|
|
42977
|
+
publishRegistry.markDismissed(args.name);
|
|
42978
|
+
job.childProc.kill("SIGTERM");
|
|
42979
|
+
} else if (project.publishJob) {
|
|
42980
|
+
deps.logger?.info("app-builder.publish.dismiss.interrupted", {
|
|
42981
|
+
name: args.name,
|
|
42982
|
+
jobId: project.publishJob.jobId,
|
|
42983
|
+
stage: project.publishJob.stage
|
|
42984
|
+
});
|
|
42985
|
+
await store.clearPublishJob(args.name);
|
|
42986
|
+
const current = getStageOf(args.name);
|
|
42987
|
+
await setStageAndBroadcast(args.name, current.stage, current.reason);
|
|
42988
|
+
}
|
|
42989
|
+
return { response: { ok: true } };
|
|
42990
|
+
};
|
|
42426
42991
|
return {
|
|
42427
42992
|
"appBuilder:listProjects": listProjects,
|
|
42428
42993
|
"appBuilder:createProject": createProject,
|
|
@@ -42431,13 +42996,15 @@ function buildAppBuilderHandlers(deps) {
|
|
|
42431
42996
|
"appBuilder:setProdUrl": setProdUrl,
|
|
42432
42997
|
"appBuilder:startDevServer": startDevServer,
|
|
42433
42998
|
"appBuilder:reportStage": reportStage,
|
|
42434
|
-
"appBuilder:stopDevServer": stopDevServer
|
|
42999
|
+
"appBuilder:stopDevServer": stopDevServer,
|
|
43000
|
+
"appBuilder:publish": publish,
|
|
43001
|
+
"appBuilder:dismissPublishJob": dismissPublishJob
|
|
42435
43002
|
};
|
|
42436
43003
|
}
|
|
42437
43004
|
|
|
42438
43005
|
// src/extension/registry.ts
|
|
42439
43006
|
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
42440
|
-
var
|
|
43007
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
42441
43008
|
async function loadAll(root) {
|
|
42442
43009
|
let entries;
|
|
42443
43010
|
try {
|
|
@@ -42450,13 +43017,13 @@ async function loadAll(root) {
|
|
|
42450
43017
|
for (const ent of entries) {
|
|
42451
43018
|
if (!ent.isDirectory()) continue;
|
|
42452
43019
|
if (ent.name.startsWith(".")) continue;
|
|
42453
|
-
records.push(await loadOne(
|
|
43020
|
+
records.push(await loadOne(import_node_path41.default.join(root, ent.name), ent.name));
|
|
42454
43021
|
}
|
|
42455
43022
|
records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
|
|
42456
43023
|
return records;
|
|
42457
43024
|
}
|
|
42458
43025
|
async function loadOne(dir, dirName) {
|
|
42459
|
-
const manifestPath =
|
|
43026
|
+
const manifestPath = import_node_path41.default.join(dir, "manifest.json");
|
|
42460
43027
|
let raw;
|
|
42461
43028
|
try {
|
|
42462
43029
|
raw = await import_promises8.default.readFile(manifestPath, "utf8");
|
|
@@ -42501,7 +43068,7 @@ async function loadOne(dir, dirName) {
|
|
|
42501
43068
|
|
|
42502
43069
|
// src/extension/uninstall.ts
|
|
42503
43070
|
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
42504
|
-
var
|
|
43071
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
42505
43072
|
var UninstallError = class extends Error {
|
|
42506
43073
|
constructor(code, message) {
|
|
42507
43074
|
super(message);
|
|
@@ -42510,7 +43077,7 @@ var UninstallError = class extends Error {
|
|
|
42510
43077
|
code;
|
|
42511
43078
|
};
|
|
42512
43079
|
async function uninstall(deps) {
|
|
42513
|
-
const dir =
|
|
43080
|
+
const dir = import_node_path42.default.join(deps.root, deps.extId);
|
|
42514
43081
|
try {
|
|
42515
43082
|
await import_promises9.default.access(dir);
|
|
42516
43083
|
} catch {
|
|
@@ -42575,15 +43142,20 @@ function buildMethodHandlers(deps) {
|
|
|
42575
43142
|
sessionManager: deps.manager,
|
|
42576
43143
|
getTunnelUrl: deps.getTunnelUrl,
|
|
42577
43144
|
broadcastProjectUpdated: deps.broadcastProjectUpdated,
|
|
42578
|
-
logger: deps.logger
|
|
43145
|
+
logger: deps.logger,
|
|
43146
|
+
// 发布上线脚手架化 (spec 2026-06-03 §5.2):
|
|
43147
|
+
// personaRoot 用于拼 publish.sh 绝对路径;broadcastSessionFrame 用于失败回灌路径
|
|
43148
|
+
// (runner 调 manager.send 后逐帧 dispatch 到 transport)。
|
|
43149
|
+
personaRoot: deps.appBuilderPersonaRoot,
|
|
43150
|
+
broadcastSessionFrame: deps.broadcastSessionFrame
|
|
42579
43151
|
})
|
|
42580
43152
|
};
|
|
42581
43153
|
}
|
|
42582
43154
|
|
|
42583
43155
|
// src/app-builder/project-store.ts
|
|
42584
|
-
var
|
|
42585
|
-
var
|
|
42586
|
-
var
|
|
43156
|
+
var import_node_fs30 = require("fs");
|
|
43157
|
+
var import_node_child_process9 = require("child_process");
|
|
43158
|
+
var import_node_path43 = require("path");
|
|
42587
43159
|
init_protocol();
|
|
42588
43160
|
var PROJECTS_DIR = "projects";
|
|
42589
43161
|
var META_FILE = ".clawd-project.json";
|
|
@@ -42598,19 +43170,19 @@ var ProjectStore = class {
|
|
|
42598
43170
|
personaRoot;
|
|
42599
43171
|
/** projects/<name>/.clawd-project.json 路径 */
|
|
42600
43172
|
metaPath(name) {
|
|
42601
|
-
return (0,
|
|
43173
|
+
return (0, import_node_path43.join)(this.projectsRoot(), name, META_FILE);
|
|
42602
43174
|
}
|
|
42603
43175
|
/** projects/<name>/ 目录路径(cwd 用) */
|
|
42604
43176
|
projectDir(name) {
|
|
42605
|
-
return (0,
|
|
43177
|
+
return (0, import_node_path43.join)(this.projectsRoot(), name);
|
|
42606
43178
|
}
|
|
42607
43179
|
projectsRoot() {
|
|
42608
|
-
return (0,
|
|
43180
|
+
return (0, import_node_path43.join)(this.personaRoot, PROJECTS_DIR);
|
|
42609
43181
|
}
|
|
42610
43182
|
async list() {
|
|
42611
43183
|
let entries;
|
|
42612
43184
|
try {
|
|
42613
|
-
entries = await
|
|
43185
|
+
entries = await import_node_fs30.promises.readdir(this.projectsRoot());
|
|
42614
43186
|
} catch (err) {
|
|
42615
43187
|
if (err.code === "ENOENT") return [];
|
|
42616
43188
|
throw err;
|
|
@@ -42618,7 +43190,7 @@ var ProjectStore = class {
|
|
|
42618
43190
|
const out = [];
|
|
42619
43191
|
for (const name of entries) {
|
|
42620
43192
|
try {
|
|
42621
|
-
const raw = await
|
|
43193
|
+
const raw = await import_node_fs30.promises.readFile(this.metaPath(name), "utf8");
|
|
42622
43194
|
const json = JSON.parse(raw);
|
|
42623
43195
|
let migrated = false;
|
|
42624
43196
|
if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
|
|
@@ -42629,7 +43201,7 @@ var ProjectStore = class {
|
|
|
42629
43201
|
if (parsed.success) {
|
|
42630
43202
|
out.push(parsed.data);
|
|
42631
43203
|
if (migrated) {
|
|
42632
|
-
void
|
|
43204
|
+
void import_node_fs30.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
|
|
42633
43205
|
});
|
|
42634
43206
|
}
|
|
42635
43207
|
}
|
|
@@ -42667,8 +43239,8 @@ var ProjectStore = class {
|
|
|
42667
43239
|
throw new Error(`invalid name "${name}": ${validated.error.message}`);
|
|
42668
43240
|
}
|
|
42669
43241
|
const dir = this.projectDir(name);
|
|
42670
|
-
await
|
|
42671
|
-
await
|
|
43242
|
+
await import_node_fs30.promises.mkdir(dir, { recursive: true });
|
|
43243
|
+
await import_node_fs30.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
|
|
42672
43244
|
return meta;
|
|
42673
43245
|
}
|
|
42674
43246
|
/**
|
|
@@ -42680,9 +43252,9 @@ var ProjectStore = class {
|
|
|
42680
43252
|
* 代码再参考"。assistant 进 project 时目录已是完整模板,直接 pnpm install 即可。
|
|
42681
43253
|
*/
|
|
42682
43254
|
async scaffold(name, template = DEFAULT_TEMPLATE) {
|
|
42683
|
-
const scriptPath = (0,
|
|
43255
|
+
const scriptPath = (0, import_node_path43.join)(this.personaRoot, SCAFFOLD_SCRIPT_REL);
|
|
42684
43256
|
return await new Promise((resolve6, reject) => {
|
|
42685
|
-
const child = (0,
|
|
43257
|
+
const child = (0, import_node_child_process9.spawn)("bash", [scriptPath, name, template], {
|
|
42686
43258
|
cwd: this.personaRoot,
|
|
42687
43259
|
env: { ...process.env, PATH: process.env.PATH ?? "" },
|
|
42688
43260
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -42712,7 +43284,7 @@ var ProjectStore = class {
|
|
|
42712
43284
|
}
|
|
42713
43285
|
async delete(name) {
|
|
42714
43286
|
const dir = this.projectDir(name);
|
|
42715
|
-
await
|
|
43287
|
+
await import_node_fs30.promises.rm(dir, { recursive: true, force: true });
|
|
42716
43288
|
}
|
|
42717
43289
|
async updatePort(name, newPort) {
|
|
42718
43290
|
if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
|
|
@@ -42728,7 +43300,7 @@ var ProjectStore = class {
|
|
|
42728
43300
|
throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
|
|
42729
43301
|
}
|
|
42730
43302
|
const updated = { ...target, port: newPort };
|
|
42731
|
-
await
|
|
43303
|
+
await import_node_fs30.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
42732
43304
|
return updated;
|
|
42733
43305
|
}
|
|
42734
43306
|
/**
|
|
@@ -42745,13 +43317,49 @@ var ProjectStore = class {
|
|
|
42745
43317
|
if (!validated.success) {
|
|
42746
43318
|
throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
|
|
42747
43319
|
}
|
|
42748
|
-
await
|
|
43320
|
+
await import_node_fs30.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
43321
|
+
return validated.data;
|
|
43322
|
+
}
|
|
43323
|
+
/**
|
|
43324
|
+
* 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.1.4):
|
|
43325
|
+
* 写 publishJob 字段到 .clawd-project.json。仅承载 in-flight 状态 ——
|
|
43326
|
+
* - daemon 收到第一个 ::stage::build marker 时写入
|
|
43327
|
+
* - 每次 stage 切换更新 stage 字段(jobId / startedAt 不变)
|
|
43328
|
+
* - 成功 / 失败 / dismiss 时调 clearPublishJob 清掉
|
|
43329
|
+
* - 只有 daemon 崩才会留残骸
|
|
43330
|
+
* 写入时保留所有其他字段(prodUrl 等不受影响)。
|
|
43331
|
+
*/
|
|
43332
|
+
async setPublishJob(name, job) {
|
|
43333
|
+
const list = await this.list();
|
|
43334
|
+
const target = list.find((p2) => p2.name === name);
|
|
43335
|
+
if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
|
|
43336
|
+
const updated = { ...target, publishJob: job };
|
|
43337
|
+
const validated = ProjectMetadataSchema.safeParse(updated);
|
|
43338
|
+
if (!validated.success) {
|
|
43339
|
+
throw new Error(`invalid publishJob: ${validated.error.message}`);
|
|
43340
|
+
}
|
|
43341
|
+
await import_node_fs30.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
43342
|
+
return validated.data;
|
|
43343
|
+
}
|
|
43344
|
+
/** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
|
|
43345
|
+
async clearPublishJob(name) {
|
|
43346
|
+
const list = await this.list();
|
|
43347
|
+
const target = list.find((p2) => p2.name === name);
|
|
43348
|
+
if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
|
|
43349
|
+
if (!target.publishJob) return target;
|
|
43350
|
+
const { publishJob: _drop, ...rest } = target;
|
|
43351
|
+
void _drop;
|
|
43352
|
+
const validated = ProjectMetadataSchema.safeParse(rest);
|
|
43353
|
+
if (!validated.success) {
|
|
43354
|
+
throw new Error(`failed to clear publishJob: ${validated.error.message}`);
|
|
43355
|
+
}
|
|
43356
|
+
await import_node_fs30.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
42749
43357
|
return validated.data;
|
|
42750
43358
|
}
|
|
42751
43359
|
};
|
|
42752
43360
|
|
|
42753
43361
|
// src/app-builder/dev-server-supervisor.ts
|
|
42754
|
-
var
|
|
43362
|
+
var import_node_child_process10 = require("child_process");
|
|
42755
43363
|
var import_node_events2 = require("events");
|
|
42756
43364
|
var DEFAULT_READY_PATTERN = /Local:\s+https?:\/\/|Nest application successfully started|server listening on/i;
|
|
42757
43365
|
var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
@@ -42788,7 +43396,7 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
|
42788
43396
|
tunnelHost: args.tunnelHost,
|
|
42789
43397
|
devCommand: cmd
|
|
42790
43398
|
});
|
|
42791
|
-
const child = (0,
|
|
43399
|
+
const child = (0, import_node_child_process10.spawn)("sh", ["-c", cmd], {
|
|
42792
43400
|
cwd: args.cwd,
|
|
42793
43401
|
env,
|
|
42794
43402
|
stdio: "pipe",
|
|
@@ -43158,7 +43766,11 @@ var METHOD_GRANT_MAP = {
|
|
|
43158
43766
|
"appBuilder:reportStage": ADMIN_ANY,
|
|
43159
43767
|
// 显式控制 fix (spec 2026-06-02-app-builder-explicit-dev-server-control):用户点"收起预览"触发停 dev server,
|
|
43160
43768
|
// 对称 startDevServer。同时砍了 setActiveSession(dev server 不再 follow-the-eyes)。
|
|
43161
|
-
"appBuilder:stopDevServer": ADMIN_ANY
|
|
43769
|
+
"appBuilder:stopDevServer": ADMIN_ANY,
|
|
43770
|
+
// 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design):
|
|
43771
|
+
// owner-only:PreviewPane「发布上线」按钮直接调 publish;UI [取消] / interrupted 恢复按钮调 dismissPublishJob。
|
|
43772
|
+
"appBuilder:publish": ADMIN_ANY,
|
|
43773
|
+
"appBuilder:dismissPublishJob": ADMIN_ANY
|
|
43162
43774
|
};
|
|
43163
43775
|
function computeGrantForFrame(method, frame) {
|
|
43164
43776
|
const rule = METHOD_GRANT_MAP[method];
|
|
@@ -43174,8 +43786,8 @@ function computeGrantForFrame(method, frame) {
|
|
|
43174
43786
|
}
|
|
43175
43787
|
|
|
43176
43788
|
// src/extension/runtime.ts
|
|
43177
|
-
var
|
|
43178
|
-
var
|
|
43789
|
+
var import_node_child_process11 = require("child_process");
|
|
43790
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
43179
43791
|
var import_promises10 = require("timers/promises");
|
|
43180
43792
|
|
|
43181
43793
|
// src/extension/port-allocator.ts
|
|
@@ -43276,13 +43888,13 @@ var Runtime = class {
|
|
|
43276
43888
|
/\$CLAWOS_EXT_PORT/g,
|
|
43277
43889
|
String(port)
|
|
43278
43890
|
);
|
|
43279
|
-
const dir =
|
|
43891
|
+
const dir = import_node_path44.default.join(this.root, extId);
|
|
43280
43892
|
const env = {
|
|
43281
43893
|
...process.env,
|
|
43282
43894
|
CLAWOS_EXT_PORT: String(port),
|
|
43283
43895
|
CLAWOS_EXT_ID: extId
|
|
43284
43896
|
};
|
|
43285
|
-
const child = (0,
|
|
43897
|
+
const child = (0, import_node_child_process11.spawn)("sh", ["-c", cmd], {
|
|
43286
43898
|
cwd: dir,
|
|
43287
43899
|
env,
|
|
43288
43900
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -43388,7 +44000,7 @@ ${handle.stderrTail}`
|
|
|
43388
44000
|
|
|
43389
44001
|
// src/extension/published-channels.ts
|
|
43390
44002
|
var import_promises11 = __toESM(require("fs/promises"), 1);
|
|
43391
|
-
var
|
|
44003
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
43392
44004
|
init_zod();
|
|
43393
44005
|
var PublishedChannelsError = class extends Error {
|
|
43394
44006
|
constructor(code, message) {
|
|
@@ -43487,7 +44099,7 @@ var PublishedChannelStore = class {
|
|
|
43487
44099
|
)
|
|
43488
44100
|
};
|
|
43489
44101
|
const tmp = `${this.filePath}.tmp`;
|
|
43490
|
-
await import_promises11.default.mkdir(
|
|
44102
|
+
await import_promises11.default.mkdir(import_node_path45.default.dirname(this.filePath), { recursive: true });
|
|
43491
44103
|
await import_promises11.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
43492
44104
|
await import_promises11.default.rename(tmp, this.filePath);
|
|
43493
44105
|
}
|
|
@@ -43495,7 +44107,7 @@ var PublishedChannelStore = class {
|
|
|
43495
44107
|
|
|
43496
44108
|
// src/extension/bundle-cache.ts
|
|
43497
44109
|
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
43498
|
-
var
|
|
44110
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
43499
44111
|
var BundleCache = class {
|
|
43500
44112
|
constructor(rootDir) {
|
|
43501
44113
|
this.rootDir = rootDir;
|
|
@@ -43504,14 +44116,14 @@ var BundleCache = class {
|
|
|
43504
44116
|
/** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
|
|
43505
44117
|
async write(snapshotHash, buffer) {
|
|
43506
44118
|
await import_promises12.default.mkdir(this.rootDir, { recursive: true });
|
|
43507
|
-
const file =
|
|
44119
|
+
const file = import_node_path46.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
43508
44120
|
const tmp = `${file}.tmp`;
|
|
43509
44121
|
await import_promises12.default.writeFile(tmp, buffer, { mode: 384 });
|
|
43510
44122
|
await import_promises12.default.rename(tmp, file);
|
|
43511
44123
|
}
|
|
43512
44124
|
/** Returns the bundle bytes, or null when the file doesn't exist. */
|
|
43513
44125
|
async read(snapshotHash) {
|
|
43514
|
-
const file =
|
|
44126
|
+
const file = import_node_path46.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
43515
44127
|
try {
|
|
43516
44128
|
return await import_promises12.default.readFile(file);
|
|
43517
44129
|
} catch (e) {
|
|
@@ -43521,7 +44133,7 @@ var BundleCache = class {
|
|
|
43521
44133
|
}
|
|
43522
44134
|
/** Idempotent — missing file is not an error. */
|
|
43523
44135
|
async delete(snapshotHash) {
|
|
43524
|
-
const file =
|
|
44136
|
+
const file = import_node_path46.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
43525
44137
|
await import_promises12.default.rm(file, { force: true });
|
|
43526
44138
|
}
|
|
43527
44139
|
};
|
|
@@ -43530,7 +44142,7 @@ var BundleCache = class {
|
|
|
43530
44142
|
async function startDaemon(config) {
|
|
43531
44143
|
const logger = createLogger({
|
|
43532
44144
|
level: config.logLevel,
|
|
43533
|
-
file:
|
|
44145
|
+
file: import_node_path47.default.join(config.dataDir, "clawd.log")
|
|
43534
44146
|
});
|
|
43535
44147
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
43536
44148
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
@@ -43666,10 +44278,11 @@ async function startDaemon(config) {
|
|
|
43666
44278
|
const agents = new AgentsScanner();
|
|
43667
44279
|
const history = new ClaudeHistoryReader();
|
|
43668
44280
|
let transport = null;
|
|
43669
|
-
const personaStore = new PersonaStore(
|
|
44281
|
+
const personaStore = new PersonaStore(import_node_path47.default.join(config.dataDir, "personas"));
|
|
43670
44282
|
const defaultsRoot = findDefaultsRoot();
|
|
43671
44283
|
if (defaultsRoot) {
|
|
43672
44284
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
44285
|
+
refreshDaemonManagedDirs({ store: personaStore, defaultsRoot, logger });
|
|
43673
44286
|
} else {
|
|
43674
44287
|
logger.warn("persona.seed.skip", { reason: "defaults-root-not-found" });
|
|
43675
44288
|
}
|
|
@@ -43683,7 +44296,7 @@ async function startDaemon(config) {
|
|
|
43683
44296
|
getAdapter,
|
|
43684
44297
|
historyReader: history,
|
|
43685
44298
|
dataDir: config.dataDir,
|
|
43686
|
-
personaRoot:
|
|
44299
|
+
personaRoot: import_node_path47.default.join(config.dataDir, "personas"),
|
|
43687
44300
|
personaStore,
|
|
43688
44301
|
ownerDisplayName,
|
|
43689
44302
|
ownerPrincipalId,
|
|
@@ -43712,10 +44325,10 @@ async function startDaemon(config) {
|
|
|
43712
44325
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
43713
44326
|
attachmentGroup: {
|
|
43714
44327
|
onFileEdit: (input) => {
|
|
43715
|
-
const absPath =
|
|
44328
|
+
const absPath = import_node_path47.default.isAbsolute(input.relPath) ? input.relPath : import_node_path47.default.join(input.cwd, input.relPath);
|
|
43716
44329
|
let size = 0;
|
|
43717
44330
|
try {
|
|
43718
|
-
size =
|
|
44331
|
+
size = import_node_fs31.default.statSync(absPath).size;
|
|
43719
44332
|
} catch (err) {
|
|
43720
44333
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
43721
44334
|
sessionId: input.sessionId,
|
|
@@ -43845,8 +44458,13 @@ async function startDaemon(config) {
|
|
|
43845
44458
|
getCallbackUrl: () => `http://127.0.0.1:${config.port}/auth/callback`
|
|
43846
44459
|
});
|
|
43847
44460
|
const appBuilderStore = new ProjectStore(
|
|
43848
|
-
|
|
44461
|
+
import_node_path47.default.join(config.dataDir, "personas/persona-app-builder")
|
|
43849
44462
|
);
|
|
44463
|
+
recoverInterruptedPublishJobs(appBuilderStore, logger).catch((err) => {
|
|
44464
|
+
logger.warn("app-builder.publish.recover-startup-failed", {
|
|
44465
|
+
error: err instanceof Error ? err.message : String(err)
|
|
44466
|
+
});
|
|
44467
|
+
});
|
|
43850
44468
|
const devServerSupervisor = new DevServerSupervisor();
|
|
43851
44469
|
devServerSupervisor.setLogger(logger);
|
|
43852
44470
|
const appBuilderPortRange = [];
|
|
@@ -43893,10 +44511,10 @@ async function startDaemon(config) {
|
|
|
43893
44511
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
43894
44512
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
43895
44513
|
// guest path guard:candidate 必须在 personaRoot 子树下
|
|
43896
|
-
personaRoot:
|
|
44514
|
+
personaRoot: import_node_path47.default.join(config.dataDir, "personas")
|
|
43897
44515
|
},
|
|
43898
44516
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
43899
|
-
personaRoot:
|
|
44517
|
+
personaRoot: import_node_path47.default.join(config.dataDir, "personas"),
|
|
43900
44518
|
// capability:list / delete handler 依赖
|
|
43901
44519
|
capabilityManager,
|
|
43902
44520
|
// v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
|
|
@@ -43968,6 +44586,27 @@ async function startDaemon(config) {
|
|
|
43968
44586
|
// 单栏 refactor §5.6.2 stage fix:appBuilder:project-updated 帧通过 wsServer broadcast。
|
|
43969
44587
|
// 推给所有 owner 连接(picker 可能没订阅特定 session,全局 broadcast 最稳)。
|
|
43970
44588
|
broadcastProjectUpdated: (event) => wsServer?.broadcastToOwners(event),
|
|
44589
|
+
// 发布上线脚手架化 (spec 2026-06-03 §5.2):
|
|
44590
|
+
// appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
|
|
44591
|
+
// dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
|
|
44592
|
+
appBuilderPersonaRoot: import_node_path47.default.join(config.dataDir, "personas", "persona-app-builder"),
|
|
44593
|
+
// 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
|
|
44594
|
+
// 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
|
|
44595
|
+
// 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
|
|
44596
|
+
broadcastSessionFrame: (frame, target) => {
|
|
44597
|
+
if (target === "all") {
|
|
44598
|
+
transport?.broadcastAll(frame);
|
|
44599
|
+
return;
|
|
44600
|
+
}
|
|
44601
|
+
const sid = frame.sessionId;
|
|
44602
|
+
if (!sid) return;
|
|
44603
|
+
if (target === "first-subscriber") {
|
|
44604
|
+
const handle = transport?.firstSubscriber(sid);
|
|
44605
|
+
if (handle) handle.send(frame);
|
|
44606
|
+
return;
|
|
44607
|
+
}
|
|
44608
|
+
transport?.broadcastToSession(sid, frame);
|
|
44609
|
+
},
|
|
43971
44610
|
logger
|
|
43972
44611
|
});
|
|
43973
44612
|
let handlers = makeHandlers();
|
|
@@ -44198,8 +44837,8 @@ async function startDaemon(config) {
|
|
|
44198
44837
|
const lines = [
|
|
44199
44838
|
`Tunnel: ${r.url}`,
|
|
44200
44839
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
44201
|
-
`Frpc config: ${
|
|
44202
|
-
`Frpc log: ${
|
|
44840
|
+
`Frpc config: ${import_node_path47.default.join(config.dataDir, "frpc.toml")}`,
|
|
44841
|
+
`Frpc log: ${import_node_path47.default.join(config.dataDir, "frpc.log")}`
|
|
44203
44842
|
];
|
|
44204
44843
|
const width = Math.max(...lines.map((l) => l.length));
|
|
44205
44844
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -44212,8 +44851,8 @@ ${bar}
|
|
|
44212
44851
|
|
|
44213
44852
|
`);
|
|
44214
44853
|
try {
|
|
44215
|
-
const connectPath =
|
|
44216
|
-
|
|
44854
|
+
const connectPath = import_node_path47.default.join(config.dataDir, "connect.txt");
|
|
44855
|
+
import_node_fs31.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
44217
44856
|
} catch {
|
|
44218
44857
|
}
|
|
44219
44858
|
} catch (err) {
|
|
@@ -44284,9 +44923,9 @@ ${bar}
|
|
|
44284
44923
|
};
|
|
44285
44924
|
}
|
|
44286
44925
|
function migrateDropPersonsDir(dataDir) {
|
|
44287
|
-
const dir =
|
|
44926
|
+
const dir = import_node_path47.default.join(dataDir, "persons");
|
|
44288
44927
|
try {
|
|
44289
|
-
|
|
44928
|
+
import_node_fs31.default.rmSync(dir, { recursive: true, force: true });
|
|
44290
44929
|
} catch {
|
|
44291
44930
|
}
|
|
44292
44931
|
}
|