aitable-workflow-server 0.1.17 → 0.1.18-beta.1786440467350

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +311 -100
  2. package/dist/index.js +11 -13
  3. package/package.json +5 -5
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- import http, { ServerResponse, IncomingMessage } from 'node:http';
2
- import { ProjectDTO, ProjectFileDTO, DesignArtifactManifestDTO, DesignArtifactValidationDTO, ProjectStateDTO, StageIdDTO, ProjectTaskDTO, RunExecutionEventDTO, RunSnapshotDTO, RunEventSink, App, RunEventDTO, ProjectExportDTO, ProjectWorkflowDTO, CreateWorkflowResponseDTO, AgentType, AgentExecutor, DwsRunner, RunScheduleDTO, RunRecordsPageDTO, ApiError, RunControlResultDTO } from 'aitable-workflow-core';
3
- import { Context, Hono } from 'hono';
1
+ import http, { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { ProjectDTO, ProjectFileDTO, DesignArtifactManifestDTO, DesignArtifactValidationDTO, ProjectExportDTO, ProjectStateDTO, StageIdDTO, ProjectTaskDTO, RunExecutionEventDTO, RunSnapshotDTO, RunEventSink, App, RunEventDTO, CreateWorkflowResponseDTO, ProjectWorkflowDTO, AgentType, AgentExecutor, DwsRunner, RunScheduleDTO, RunRecordsPageDTO, ApiError, RunControlResultDTO } from 'aitable-workflow-core';
4
3
 
5
4
  type WorkspaceOptions = {
6
5
  dataRoot?: string;
@@ -31,53 +30,6 @@ declare function listProjectFiles(project: ProjectDTO): Promise<ProjectFileDTO[]
31
30
  declare function configPath(project: ProjectDTO): string;
32
31
  declare function buildAITableUrl(baseId: string, sheetId: string, viewId?: string): string;
33
32
 
34
- interface WorkflowInstanceStore {
35
- getState(project: ProjectDTO): Promise<ProjectStateDTO>;
36
- getRecoverableState(project: ProjectDTO): Promise<ProjectStateDTO>;
37
- createTask(project: ProjectDTO, stage: StageIdDTO): Promise<ProjectTaskDTO>;
38
- updateTask(project: ProjectDTO, taskId: string, patch: Partial<ProjectTaskDTO>): Promise<ProjectTaskDTO>;
39
- clearActiveTask(project: ProjectDTO, taskId: string): Promise<ProjectStateDTO>;
40
- appendRunSnapshotEvent(project: ProjectDTO, event: RunExecutionEventDTO): Promise<ProjectStateDTO>;
41
- updateRunSnapshotRecord(project: ProjectDTO, itemId: string, patch: Partial<Pick<RunSnapshotDTO["records"][number], "state" | "status" | "currentStepId">>, logText?: string): Promise<ProjectStateDTO>;
42
- /**
43
- * WP-10-002:删除项目的运行时状态(state.json + tasks/*)。
44
- * 由 DELETE /api/projects/:id 的级联清理调用(server.ts feature-detect)。
45
- * PG 实现删 project_state 行;local 实现删 runtime/state.json 与 tasks 目录。
46
- */
47
- deleteProjectState(projectId: string): Promise<void>;
48
- }
49
- declare class LocalWorkflowInstanceStore implements WorkflowInstanceStore {
50
- private readonly workspace;
51
- constructor(workspace: Workspace);
52
- getState(project: ProjectDTO): Promise<ProjectStateDTO>;
53
- getRecoverableState(project: ProjectDTO): Promise<ProjectStateDTO>;
54
- createTask(project: ProjectDTO, stage: StageIdDTO): Promise<ProjectTaskDTO>;
55
- updateTask(project: ProjectDTO, taskId: string, patch: Partial<ProjectTaskDTO>): Promise<ProjectTaskDTO>;
56
- clearActiveTask(project: ProjectDTO, taskId: string): Promise<ProjectStateDTO>;
57
- appendRunSnapshotEvent(project: ProjectDTO, event: RunExecutionEventDTO): Promise<ProjectStateDTO>;
58
- updateRunSnapshotRecord(project: ProjectDTO, itemId: string, patch: Partial<Pick<RunSnapshotDTO["records"][number], "state" | "status" | "currentStepId">>, logText?: string): Promise<ProjectStateDTO>;
59
- deleteProjectState(projectId: string): Promise<void>;
60
- }
61
-
62
- type RunExecutionEvent = Parameters<RunEventSink>[0];
63
-
64
- type RunAppFactory = (configPath: string, onRunEvent?: (event: RunExecutionEvent) => void) => Promise<App>;
65
- type RunSession = {
66
- taskId: string;
67
- subscribers: Set<ServerResponse>;
68
- events: RunEventDTO[];
69
- app: App | null;
70
- snapshotWrite: Promise<unknown>;
71
- };
72
- type ServerRuntime = {
73
- appFactory: RunAppFactory;
74
- runSessions: Map<string, RunSession>;
75
- };
76
- type RunSseWriter = (res: ServerResponse, event: string, data: unknown) => void;
77
- declare function createServerRuntime(appFactory: RunAppFactory): ServerRuntime;
78
- declare function handleRunRuntime(projectId: string, workspace: Workspace, projectStateStore: WorkflowInstanceStore, runtime: ServerRuntime, res: ServerResponse, writeSse: RunSseWriter): Promise<void>;
79
- declare function overlayRunSessionState(state: ProjectStateDTO, session: RunSession | undefined): ProjectStateDTO;
80
-
81
33
  type ActorContext = {
82
34
  actorId?: string;
83
35
  /**
@@ -142,6 +94,108 @@ declare class LocalProjectRepository implements ProjectRepository {
142
94
  syncHotFields(): Promise<void>;
143
95
  }
144
96
 
97
+ /**
98
+ * Midway/koa 请求上下文的**结构化最小契约**。
99
+ *
100
+ * 为什么不直接用 `Context from "@midwayjs/koa"`:controller/middleware 真正用到的只有
101
+ * 下面这几个字段(本质上是「原生 req/res + 一点路由信息 + 一个请求级 state 口袋」)。
102
+ * 用结构化类型把这个面收窄到最小,好处是本包的 HTTP 面不与某个具体 web 层的 Context
103
+ * 形状绑定 —— 换宿主、或宿主升级导致 Context 类型变动,都不会波及这里。
104
+ * (历史上还有一层原因:gateway 曾是 egg 宿主 `@midwayjs/web`,Context 类型与 koa 不同;
105
+ * 现在两个宿主都是 `@midwayjs/koa`,但收窄契约这件事本身仍然值得保留。)
106
+ *
107
+ * 响应约定(与 Hono 时期字节一致的关键):所有 controller 方法一律
108
+ * `ctx.respond = false`,再经 `http/respond.ts` / `http/sse.ts` 的原生助手写
109
+ * `ctx.res`,而不是 return 值让框架序列化。这样 JSON / SSE / 错误三者同构,
110
+ * 也绕开了 koa「自动 end / 自动序列化」带来的字节差异。
111
+ */
112
+
113
+ /** 中间件写入、controller 读取的请求级缓存(取代 Hono 的 `c.env.scope` / `c.env.project`)。 */
114
+ type ServerCtxState = {
115
+ scope?: ActorContext;
116
+ project?: ProjectDTO;
117
+ };
118
+ type ServerCtx = {
119
+ /** koa 暴露的原生 req/res —— 既有 handler(design/setup/stage/run、SSE)直接吃这两个。 */
120
+ req: IncomingMessage;
121
+ res: ServerResponse;
122
+ path: string;
123
+ method: string;
124
+ /** 置 false 后 koa 不再接管响应,由我们自己写 `res`。 */
125
+ respond: boolean;
126
+ state: ServerCtxState;
127
+ params: Record<string, string>;
128
+ /**
129
+ * 解析后的 query string(koa 的 `ctx.query`,形状同 node 的 `ParsedUrlQuery`)。
130
+ * 重复参数会给出数组,故读取一律经 {@link firstQueryValue} 取首个 —— 与 Hono
131
+ * `c.req.query(k)` 的语义对齐(那边也是取首个)。
132
+ */
133
+ query?: Record<string, string | string[] | undefined>;
134
+ };
135
+ /**
136
+ * 从 `ctx.query` 取单值:数组取首个,空串视为缺省。
137
+ * 存在的必要性:koa/node 对重复 query key 给数组,而既有契约(Hono 时期)是取首个。
138
+ */
139
+ declare function firstQueryValue(value: string | string[] | undefined): string | null;
140
+ /** `ctx.state.scope` 由 IdentityMiddleware 保证已写入;此处收敛非空断言。 */
141
+ declare function scopeOf(ctx: ServerCtx): ActorContext;
142
+
143
+ /** 宿主 app 上我们真正用到的能力(收窄到最小面,不与具体 web 层的 Application 类型绑定)。 */
144
+ type MiddlewareHost = {
145
+ useMiddleware: (middleware: unknown[]) => void;
146
+ };
147
+ declare class ServerConfiguration {
148
+ app: MiddlewareHost;
149
+ onReady(): Promise<void>;
150
+ }
151
+
152
+ interface WorkflowInstanceStore {
153
+ getState(project: ProjectDTO): Promise<ProjectStateDTO>;
154
+ getRecoverableState(project: ProjectDTO): Promise<ProjectStateDTO>;
155
+ createTask(project: ProjectDTO, stage: StageIdDTO): Promise<ProjectTaskDTO>;
156
+ updateTask(project: ProjectDTO, taskId: string, patch: Partial<ProjectTaskDTO>): Promise<ProjectTaskDTO>;
157
+ clearActiveTask(project: ProjectDTO, taskId: string): Promise<ProjectStateDTO>;
158
+ appendRunSnapshotEvent(project: ProjectDTO, event: RunExecutionEventDTO): Promise<ProjectStateDTO>;
159
+ updateRunSnapshotRecord(project: ProjectDTO, itemId: string, patch: Partial<Pick<RunSnapshotDTO["records"][number], "state" | "status" | "currentStepId">>, logText?: string): Promise<ProjectStateDTO>;
160
+ /**
161
+ * WP-10-002:删除项目的运行时状态(state.json + tasks/*)。
162
+ * 由 DELETE /api/projects/:id 的级联清理调用(server.ts feature-detect)。
163
+ * PG 实现删 project_state 行;local 实现删 runtime/state.json 与 tasks 目录。
164
+ */
165
+ deleteProjectState(projectId: string): Promise<void>;
166
+ }
167
+ declare class LocalWorkflowInstanceStore implements WorkflowInstanceStore {
168
+ private readonly workspace;
169
+ constructor(workspace: Workspace);
170
+ getState(project: ProjectDTO): Promise<ProjectStateDTO>;
171
+ getRecoverableState(project: ProjectDTO): Promise<ProjectStateDTO>;
172
+ createTask(project: ProjectDTO, stage: StageIdDTO): Promise<ProjectTaskDTO>;
173
+ updateTask(project: ProjectDTO, taskId: string, patch: Partial<ProjectTaskDTO>): Promise<ProjectTaskDTO>;
174
+ clearActiveTask(project: ProjectDTO, taskId: string): Promise<ProjectStateDTO>;
175
+ appendRunSnapshotEvent(project: ProjectDTO, event: RunExecutionEventDTO): Promise<ProjectStateDTO>;
176
+ updateRunSnapshotRecord(project: ProjectDTO, itemId: string, patch: Partial<Pick<RunSnapshotDTO["records"][number], "state" | "status" | "currentStepId">>, logText?: string): Promise<ProjectStateDTO>;
177
+ deleteProjectState(projectId: string): Promise<void>;
178
+ }
179
+
180
+ type RunExecutionEvent = Parameters<RunEventSink>[0];
181
+
182
+ type RunAppFactory = (configPath: string, onRunEvent?: (event: RunExecutionEvent) => void) => Promise<App>;
183
+ type RunSession = {
184
+ taskId: string;
185
+ subscribers: Set<ServerResponse>;
186
+ events: RunEventDTO[];
187
+ app: App | null;
188
+ snapshotWrite: Promise<unknown>;
189
+ };
190
+ type ServerRuntime = {
191
+ appFactory: RunAppFactory;
192
+ runSessions: Map<string, RunSession>;
193
+ };
194
+ type RunSseWriter = (res: ServerResponse, event: string, data: unknown) => void;
195
+ declare function createServerRuntime(appFactory: RunAppFactory): ServerRuntime;
196
+ declare function handleRunRuntime(projectId: string, workspace: Workspace, projectStateStore: WorkflowInstanceStore, runtime: ServerRuntime, res: ServerResponse, writeSse: RunSseWriter): Promise<void>;
197
+ declare function overlayRunSessionState(state: ProjectStateDTO, session: RunSession | undefined): ProjectStateDTO;
198
+
145
199
  /** 带内容创建工作流的初始载荷。 */
146
200
  type CreateWorkflowInit = {
147
201
  name: string;
@@ -354,36 +408,14 @@ declare class LocalIdentityService implements IdentityService {
354
408
  resolveScope(req: IncomingMessage): Promise<ActorContext>;
355
409
  }
356
410
 
357
- /** 每请求绑定:原生 req/res、服务集合、请求级缓存(actor scope / 已解析 project)。 */
358
- type HttpEnv = {
359
- incoming: IncomingMessage;
360
- outgoing: ServerResponse;
361
- services: ServerServices;
362
- scope: ActorContext;
363
- project?: ProjectDTO;
364
- /** 既有 raw handler 已直接写出响应(JSON/SSE),适配层跳过 Response 序列化。 */
365
- rawHandled?: boolean;
366
- };
367
- type AppEnv = {
368
- Bindings: HttpEnv;
369
- };
370
- type Ctx = Context<AppEnv>;
371
- /** 与旧 sendJson 字节一致的 JSON 响应(尾部换行 + charset)。 */
372
- declare function jsonBody(c: Ctx, body: unknown, status?: number): Response;
373
- declare function errorBody(c: Ctx, status: number, code: string, message: string, details?: unknown): Response;
374
- /** 与旧 sendUnauthorized 字节一致。 */
375
- declare function unauthorized(c: Ctx): Response;
376
- /** 包装既有 (raw req/res) handler:执行后标记 rawHandled,适配层不再写 Response。 */
377
- declare function raw(fn: (c: Ctx) => Promise<void>): (c: Ctx) => Promise<Response>;
378
- /** 执行域接缝。 */
379
- declare function backendOf(c: Ctx): ExecutionBackend;
380
- /** 单项目子树公共前置:按 :projectId + actor scope 解析项目,失败经 onError 统一映射。 */
381
- declare function resolveProjectMw(c: Ctx, next: () => Promise<void>): Promise<void>;
382
-
383
- /**
384
- * 服务集合:**4 个业务接缝 + 2 个数据字段**,封口不再扩张——
385
- * local/gateway 的任何行为分化必须归入 store / execution / identity / extendApp
386
- * 之一(在对应领域服务上加方法,local 给基线实现),不允许新增顶层注入字段。
411
+ /**
412
+ * 服务集合:**3 个业务接缝 + 2 个数据字段**,封口不再扩张——
413
+ * local/gateway 的任何行为分化必须归入 store / execution / identity 之一
414
+ * (在对应领域服务上加方法,local 给基线实现),不允许新增顶层注入字段。
415
+ *
416
+ * 第 4 个接缝 `extendApp` 已随 Hono 下线一同移除:路由扩展现在是 Midway 的
417
+ * 原生能力——gateway 自己是宿主,把 `/api/auth` `/api/account` `/api/relay` 的
418
+ * controller 放在自己包里即可,不再需要本包递一个 app 出去让它挂路由。
387
419
  */
388
420
  type ServerServices = {
389
421
  workspace: Workspace;
@@ -394,9 +426,7 @@ type ServerServices = {
394
426
  execution: ExecutionBackend;
395
427
  /** ③ 身份域:请求鉴权 + actor 归属解析(local API token / gateway JWT+owner-token)。 */
396
428
  identityService: IdentityService;
397
- /** ④ 路由扩展:组合根追加自有路由(gateway 的 /api/auth /api/account /api/relay)。 */
398
- extendApp?: (app: Hono<AppEnv>) => void;
399
- /** 本地 web 静态资源目录;配置时 handleRequest 托管前端页面。 */
429
+ /** 本地 web 静态资源目录;配置时 StaticWebMiddleware 托管前端页面。 */
400
430
  webRoot?: string;
401
431
  };
402
432
  type ServerServicesOptions = {
@@ -410,8 +440,6 @@ type ServerServicesOptions = {
410
440
  execution?: ExecutionBackend;
411
441
  /** 可选注入:身份域(缺省 LocalIdentityService)。 */
412
442
  identityService?: IdentityService;
413
- /** 可选注入:路由扩展。 */
414
- extendApp?: (app: Hono<AppEnv>) => void;
415
443
  /** 本地 web 静态资源目录。 */
416
444
  webRoot?: string;
417
445
  };
@@ -431,15 +459,183 @@ declare function createServerServices(options?: ServerServicesOptions): ServerSe
431
459
  */
432
460
  declare function warnDeprecatedBootstrapKey(env?: NodeJS.ProcessEnv): boolean;
433
461
 
462
+ declare class SystemController {
463
+ ctx: ServerCtx;
464
+ serverServices: ServerServices;
465
+ health(): Promise<void>;
466
+ /**
467
+ * Aone 部署存活探测:基础镜像的 nodejsctl(status/startnginx)与 preload.sh 探测此端点,
468
+ * 要求 200 且响应体含 "success",否则判定应用未起来、不挂 nginx。
469
+ *
470
+ * 两个宿主(cli 本地 app / apps/gateway)都走本方法,返回恒定的 `"success"`。
471
+ * (历史:gateway 曾是 egg 宿主,egg-status 插件自带同名 `/check.node` 且优先级更高,
472
+ * 会遮蔽本方法而返回 `"<app> started at ..., success, online"`;去 egg 后不再有这层遮蔽,
473
+ * 响应体变为纯 `"success"`。`APP-META/.../start.sh` 只判响应体非空,两者都满足。)
474
+ */
475
+ checkNode(): Promise<void>;
476
+ /**
477
+ * 公开只读端点:下发运行时前端配置——是否启用登录(authEnabled,前端据此决定
478
+ * 是否展示登录页)+ 可选 relay 网关地址(RELAY_PUBLIC_GATEWAY_URL 注入)。
479
+ */
480
+ publicConfig(): Promise<void>;
481
+ /**
482
+ * 本机 coding agent 探测(与 CLI init 向导同源 detectAgents):local 前端 init
483
+ * 阶段据此渲染引擎选择;app 部署下前端改用机器 runtimes,不调本端点。
484
+ */
485
+ agents(): Promise<void>;
486
+ }
487
+
488
+ declare class ProjectsController {
489
+ ctx: ServerCtx;
490
+ serverServices: ServerServices;
491
+ list(): Promise<void>;
492
+ create(): Promise<void>;
493
+ remove(): Promise<void>;
494
+ updateMeta(): Promise<void>;
495
+ /** raw handler:handleInit 直接写原生 req/res(JSON/SSE),本方法不再写响应。 */
496
+ init(): Promise<void>;
497
+ getEngine(): Promise<void>;
498
+ bindEngine(): Promise<void>;
499
+ listFiles(): Promise<void>;
500
+ replaceFiles(): Promise<void>;
501
+ patchFiles(): Promise<void>;
502
+ exportProject(): Promise<void>;
503
+ validate(): Promise<void>;
504
+ getWorkflow(): Promise<void>;
505
+ resetWorkflow(): Promise<void>;
506
+ getState(): Promise<void>;
507
+ /** raw handler:handleUpdateProjectTableId 直接写原生 req/res,本方法不再写响应。 */
508
+ updateTableId(): Promise<void>;
509
+ }
510
+
511
+ declare class WorkflowsController {
512
+ ctx: ServerCtx;
513
+ serverServices: ServerServices;
514
+ /**
515
+ * 写端点共用的前置:工作流持久化未配置(无 `projectStore.workflows`)时写 501 并
516
+ * 返回 true,调用方据此早返回——与 Hono 侧 `notConfigured(c)` 守卫逐字节等价。
517
+ *
518
+ * 注意两个**只读**端点(list / get)已不再走这个守卫:无 DB 时它们改为读盘回落
519
+ * (见 {@link listLocalWorkflows} / {@link exportLocalWorkflow}),这样 local 形态
520
+ * 也能列出/导出工作流。写端点仍需 DB,保持 501。
521
+ */
522
+ private guardNotConfigured;
523
+ list(): Promise<void>;
524
+ create(): Promise<void>;
525
+ get(): Promise<void>;
526
+ remove(): Promise<void>;
527
+ replaceFiles(): Promise<void>;
528
+ updateFiles(): Promise<void>;
529
+ }
530
+
531
+ declare class StagesController {
532
+ ctx: ServerCtx;
533
+ serverServices: ServerServices;
534
+ /**
535
+ * stage 任务动作入口,按 `:stage` 分派:`setup`→handleSetup、`run`→handleRun,
536
+ * 其余(含合法 action 但未知 stage、以及非 start/continue/retry 的 action)落 404。
537
+ * 分派与 404 分支与 Hono 侧逐字节一致。
538
+ */
539
+ taskAction(): Promise<void>;
540
+ designDiscovery(): Promise<void>;
541
+ designDiscoveryConfirm(): Promise<void>;
542
+ design(): Promise<void>;
543
+ setup(): Promise<void>;
544
+ runHumanAction(): Promise<void>;
545
+ run(): Promise<void>;
546
+ runStop(): Promise<void>;
547
+ runStatus(): Promise<void>;
548
+ /** 工单列表:两模式同构读模型(local=runSnapshot / lease=PG work_items 投影)。 */
549
+ runRecords(): Promise<void>;
550
+ }
551
+
552
+ declare class CorsMiddleware {
553
+ resolve(): (ctx: ServerCtx, next: () => Promise<void>) => Promise<void>;
554
+ static getName(): string;
555
+ }
556
+
557
+ declare class ErrorMappingMiddleware {
558
+ resolve(): (ctx: ServerCtx, next: () => Promise<void>) => Promise<void>;
559
+ static getName(): string;
560
+ }
561
+
562
+ declare class IdentityMiddleware {
563
+ /**
564
+ * 属性注入(按属性名 `serverServices` 解析),由宿主在 `onReady` 里
565
+ * `registerObject("serverServices", services)` 提供。
566
+ * 刻意不用构造器注入:tsup/esbuild 不产出 `design:paramtypes`,按类型注入会失效。
567
+ */
568
+ serverServices: ServerServices;
569
+ resolve(): (ctx: ServerCtx, next: () => Promise<void>) => Promise<void>;
570
+ static getName(): string;
571
+ }
572
+
573
+ declare class ResolveProjectMiddleware {
574
+ serverServices: ServerServices;
575
+ resolve(): (ctx: ServerCtx, next: () => Promise<void>) => Promise<void>;
576
+ static getName(): string;
577
+ }
578
+
579
+ declare class StaticWebMiddleware {
580
+ serverServices: ServerServices;
581
+ resolve(): (ctx: ServerCtx, next: () => Promise<void>) => Promise<void>;
582
+ static getName(): string;
583
+ }
584
+
585
+ /**
586
+ * Midway component 命名空间 —— 由主 barrel(`./index.ts`)整体 re-export。
587
+ *
588
+ * 为什么这些类要聚在一个文件里:宿主装配写的是 `imports: [aitableServer]`,Midway 会从
589
+ * 这个**模块命名空间**上取名为 `Configuration` 的入口配置类;而 controller/middleware
590
+ * 只需被「加载过」(装饰器在类定义时就把自己登记进 Midway 的全局 module store)。
591
+ * 我们关掉了目录扫描(`moduleDetector: false`,因为本包以 tsup 单文件形态发布,磁盘上
592
+ * 没有 Midway 期望的 `src` 逐文件 js 结构),所以必须在这里显式列全,漏一个就少一段路由。
593
+ *
594
+ * 宿主用法(**传的是本模块,不是整包 barrel**——barrel 有 71 个导出,其中只有这里的
595
+ * 9 个真正会被 Midway 绑定;理由见 index.ts 上 `midwayComponent` 那条导出的注释):
596
+ * import { midwayComponent } from "aitable-workflow-server";
597
+ * @Configuration({ imports: [koa, midwayComponent] })
598
+ *
599
+ * 宿主必须在自己的 `onReady` 里注册组合根产物:
600
+ * container.registerObject("serverServices", services)
601
+ */
602
+
603
+ type midway_CorsMiddleware = CorsMiddleware;
604
+ declare const midway_CorsMiddleware: typeof CorsMiddleware;
605
+ type midway_ErrorMappingMiddleware = ErrorMappingMiddleware;
606
+ declare const midway_ErrorMappingMiddleware: typeof ErrorMappingMiddleware;
607
+ type midway_IdentityMiddleware = IdentityMiddleware;
608
+ declare const midway_IdentityMiddleware: typeof IdentityMiddleware;
609
+ type midway_ProjectsController = ProjectsController;
610
+ declare const midway_ProjectsController: typeof ProjectsController;
611
+ type midway_ResolveProjectMiddleware = ResolveProjectMiddleware;
612
+ declare const midway_ResolveProjectMiddleware: typeof ResolveProjectMiddleware;
613
+ type midway_ServerConfiguration = ServerConfiguration;
614
+ declare const midway_ServerConfiguration: typeof ServerConfiguration;
615
+ type midway_StagesController = StagesController;
616
+ declare const midway_StagesController: typeof StagesController;
617
+ type midway_StaticWebMiddleware = StaticWebMiddleware;
618
+ declare const midway_StaticWebMiddleware: typeof StaticWebMiddleware;
619
+ type midway_SystemController = SystemController;
620
+ declare const midway_SystemController: typeof SystemController;
621
+ type midway_WorkflowsController = WorkflowsController;
622
+ declare const midway_WorkflowsController: typeof WorkflowsController;
623
+ declare namespace midway {
624
+ export { ServerConfiguration as Configuration, midway_CorsMiddleware as CorsMiddleware, midway_ErrorMappingMiddleware as ErrorMappingMiddleware, midway_IdentityMiddleware as IdentityMiddleware, midway_ProjectsController as ProjectsController, midway_ResolveProjectMiddleware as ResolveProjectMiddleware, midway_ServerConfiguration as ServerConfiguration, midway_StagesController as StagesController, midway_StaticWebMiddleware as StaticWebMiddleware, midway_SystemController as SystemController, midway_WorkflowsController as WorkflowsController };
625
+ }
626
+
434
627
  /**
435
- * server.ts —— 服务入口与 node:http ↔ Hono 适配层。
628
+ * server.ts —— 服务入口(起一个 Midway + `@midwayjs/koa` 宿主并 imports 本包 component)。
436
629
  *
437
- * - 路由与中间件在 {@link createHonoApp}(app.ts);本文件仅保留:
438
- * startServer(监听 + 默认管理员种子)与 handleRequest(把原生 req/res 桥接进
439
- * Hono 应用,供 http.createServer 与测试直调)。
440
- * - 既有 raw handler(SSE / 直接写 res 的 JSON)经 env.rawHandled 标记跳过 Response 序列化。
441
- * - 本包为纯 local server:relay 网关 / PG / OSS 的装配在 apps/gateway(组合根经
442
- * options.services 注入完整 ServerServices,并自行挂载 WS upgrade)。
630
+ * - 路由与中间件全在本包的 Midway component 面(`src/midway.ts` → controller/* +
631
+ * middleware/*,由 `ServerConfiguration.onReady` 统一 useMiddleware);本文件只负责
632
+ * 「装配一个最小 koa 宿主 app」这一件事,不含任何路由/CORS/静态托管逻辑
633
+ * (迁移前它们散在 handleRequest 里,现已分别落到 CorsMiddleware / StaticWebMiddleware)。
634
+ * - 两类宿主形态对照(**同一套 `@midwayjs/koa` 装配,只差注入的 services**):
635
+ * · cli `serve` → 本文件:`imports: [koa, 本 component]`,纯 local 装配。
636
+ * · apps/gateway → 它自己的 `@Configuration`(`imports: [koa, 本 component]`,再注入
637
+ * PG repos / RelayExecutionBackend),由 Midway bootstrap 启动,**不经过 startServer**,
638
+ * 并自行挂载 WS upgrade。
443
639
  */
444
640
 
445
641
  type StartServerOptions = {
@@ -457,12 +653,24 @@ type StartServerOptions = {
457
653
  };
458
654
  declare function startServer(options?: StartServerOptions): Promise<http.Server>;
459
655
  /**
460
- * node:http ↔ Hono 请求适配:
461
- * 1. CORS / OPTIONS 预检与 local 静态托管在进入 Hono 前处理(与旧实现顺序一致)。
462
- * 2. 由原生 req 构造 fetch Request(不消费 body:raw handler 仍从原生 req 读流)。
463
- * 3. raw handler 已写出(env.rawHandled)→ 直接返回;否则把 Hono Response 序列化回 res。
656
+ * 隔离 `MIDWAY_HTTP_PORT`:**任何在同一进程内多次 boot Midway koa 的地方都必须包这一层。**
657
+ *
658
+ * `@midwayjs/koa` 的 `run()` 端口取 `process.env.MIDWAY_HTTP_PORT || 配置端口`,
659
+ * listen 成功后又把实际端口**写回**该 env。于是第二次 boot 会继承上一台的端口,
660
+ * `port: 0`「每次挑一个空闲端口」的语义被破坏,实测两种炸法:
661
+ * - 上一台还没关 → EADDRINUSE;
662
+ * - 上一台已关 → 新 server 绑到**同一个端口**,于是 baseUrl 与上一轮完全相同,
663
+ * undici 的连接池按 host:port 命中缓存,把上一台已关闭 server 的 socket 拿来复用 →
664
+ * `SocketError: other side closed`(表现为随机 case 偶发失败)。
665
+ *
666
+ * 故 boot 期间清掉它(让本次调用的配置端口说话),完成后恢复原值,避免影响进程内
667
+ * 其他读该 env 的代码。
668
+ *
669
+ * 导出是刻意的:`apps/gateway` 的测试脚手架要自己拼一个含 gateway controller 的宿主
670
+ * (见其 `__tests__/helpers/http-drive.ts`),必须复用同一个守卫 —— 之前它自己写了一份
671
+ * boot 逻辑却漏了这层,正是上面第二种炸法的来源。
464
672
  */
465
- declare function handleRequest(services: ServerServices, req: IncomingMessage, res: ServerResponse): Promise<void>;
673
+ declare function withoutLeakedMidwayPortEnv<T>(boot: () => Promise<T>): Promise<T>;
466
674
 
467
675
  /**
468
676
  * serve 配置解析层
@@ -505,10 +713,13 @@ type ServeConfig = {
505
713
  */
506
714
  declare function resolveServeConfig(overrides?: ServeConfigOverrides, env?: NodeJS.ProcessEnv): ServeConfig;
507
715
 
508
- declare function createHonoApp(): Hono<AppEnv>;
509
-
510
716
  declare function readJson<T>(req: IncomingMessage): Promise<T>;
511
717
  declare function sendJson(res: ServerResponse, body: unknown, status?: number): void;
718
+ /**
719
+ * 与 Hono `c.text()` 字节一致的纯文本响应(含 `charset=UTF-8` 大写拼写、无尾部换行)。
720
+ * 目前只服务 `/check.node`:Aone 基础镜像的 nodejsctl / preload.sh 探测此端点。
721
+ */
722
+ declare function sendText(res: ServerResponse, body: string, status?: number): void;
512
723
  declare function sendError(res: ServerResponse, status: number, code: string, message: string, details?: unknown): void;
513
724
  declare function sendUnauthorized(res: ServerResponse): void;
514
725
  declare function apiError(error: unknown): ApiError["error"];
@@ -692,4 +903,4 @@ declare class LeaseRunController implements RunController {
692
903
  /** 按执行域接缝装配控制器:注入了 runScheduler → lease,否则 local。 */
693
904
  declare function resolveRunController(services: ServerServices): RunController;
694
905
 
695
- export { type ActorContext, type AppEnv, type CreateProjectInit, type CreateProjectResult, type CreateWorkflowInit, type CreateWorkflowResult, type Ctx, DEFAULT_SERVE_HOST, DEFAULT_SERVE_PORT, type ExecutionBackend, type FilesPatch, type HttpEnv, type IdentityService, LeaseRunController, LocalExecutionBackend, LocalIdentityService, LocalProjectMutexStore, LocalProjectRepository, LocalProjectStore, LocalRunController, LocalWorkflowInstanceStore, type MaterializeOptions, type MaterializeResult, type ProjectFileKind, type ProjectMutexStore, type ProjectRepository, type ProjectStore, type RelayMachineInfo, type RunAppFactory, type RunControlResult, type RunController, type RunRecordsPage, type RunRecordsReader, type RunScheduleStatus, type RunScheduler, type RunSession, type RunSseWriter, type ServeConfig, type ServeConfigOverrides, type ServerRuntime, type ServerServices, type ServerServicesOptions, type StartServerOptions, type WorkflowExportDTO, type WorkflowInstanceStore, type WorkflowRepository, type Workspace, type WorkspaceOptions, type WriteProjectFilesResult, activeTaskOf, apiError, assertSafeProjectPath, assertSafeRelativePath, assertSafeWorkflowPath, assertWritableKind, backendOf, buildAITableUrl, cascadeDeleteProject, checksumContent, commitStageOnTerminal, configPath, createHonoApp, createProject, createServerRuntime, createServerServices, createWorkspace, deleteProject, errorBody, errorCode, errorDetails, errorMessage, errorStatus, handleRequest, handleRunRuntime, isForbiddenWritable, isStageActiveFor, isTerminalStatus, jsonBody, kindOf, listProjectFiles, listProjects, mergeTaskIntoState, normalizeProjectState, overlayRunSessionState, prepareWorkdirGuarded, raw, readDesignManifest, readJson, removeProjectFiles, replaceProjectFiles, resetProjectWorkflow, resolveProject, resolveProjectMw, resolveRunController, resolveServeConfig, sendError, sendJson, sendUnauthorized, stageStateOf, startServer, touchProject, unauthorized, updateProjectMeta, validateConfigScene, validateDesignArtifacts, warnDeprecatedBootstrapKey, workflowFileKind, writeDesignManifest, writeProjectFiles };
906
+ export { type ActorContext, type CreateProjectInit, type CreateProjectResult, type CreateWorkflowInit, type CreateWorkflowResult, DEFAULT_SERVE_HOST, DEFAULT_SERVE_PORT, type ExecutionBackend, type FilesPatch, type IdentityService, LeaseRunController, LocalExecutionBackend, LocalIdentityService, LocalProjectMutexStore, LocalProjectRepository, LocalProjectStore, LocalRunController, LocalWorkflowInstanceStore, type MaterializeOptions, type MaterializeResult, type ProjectFileKind, type ProjectMutexStore, type ProjectRepository, type ProjectStore, type RelayMachineInfo, type RunAppFactory, type RunControlResult, type RunController, type RunRecordsPage, type RunRecordsReader, type RunScheduleStatus, type RunScheduler, type RunSession, type RunSseWriter, type ServeConfig, type ServeConfigOverrides, type ServerCtx, type ServerCtxState, type ServerRuntime, type ServerServices, type ServerServicesOptions, type StartServerOptions, type WorkflowExportDTO, type WorkflowInstanceStore, type WorkflowRepository, type Workspace, type WorkspaceOptions, type WriteProjectFilesResult, activeTaskOf, apiError, assertSafeProjectPath, assertSafeRelativePath, assertSafeWorkflowPath, assertWritableKind, buildAITableUrl, cascadeDeleteProject, checksumContent, commitStageOnTerminal, configPath, createProject, createServerRuntime, createServerServices, createWorkspace, deleteProject, errorCode, errorDetails, errorMessage, errorStatus, firstQueryValue, handleRunRuntime, isForbiddenWritable, isStageActiveFor, isTerminalStatus, kindOf, listProjectFiles, listProjects, mergeTaskIntoState, midway as midwayComponent, normalizeProjectState, overlayRunSessionState, prepareWorkdirGuarded, readDesignManifest, readJson, removeProjectFiles, replaceProjectFiles, resetProjectWorkflow, resolveProject, resolveRunController, resolveServeConfig, scopeOf, sendError, sendJson, sendText, sendUnauthorized, stageStateOf, startServer, touchProject, updateProjectMeta, validateConfigScene, validateDesignArtifacts, warnDeprecatedBootstrapKey, withoutLeakedMidwayPortEnv, workflowFileKind, writeDesignManifest, writeProjectFiles };
package/dist/index.js CHANGED
@@ -1,19 +1,17 @@
1
- var xr=Object.defineProperty;var s=(e,t)=>xr(e,"name",{value:t,configurable:!0});import Qn from"http";import{Hono as Tn}from"hono";import{detectAgents as Sn}from"aitable-workflow-core";async function I(e){let t=[];for await(let o of e)t.push(Buffer.isBuffer(o)?o:Buffer.from(o));let r=Buffer.concat(t).toString("utf8").trim();return r?JSON.parse(r):{}}s(I,"readJson");function _(e,t,r=200){e.writeHead(r,{"content-type":"application/json; charset=utf-8"}),e.end(`${JSON.stringify(t)}
2
- `)}s(_,"sendJson");function q(e,t,r,o,n){_(e,{error:{code:r,message:o,details:n}},t)}s(q,"sendError");function rs(e){e.writeHead(401,{"content-type":"application/json; charset=utf-8"}),e.end(`${JSON.stringify({error:"unauthorized"})}
3
- `)}s(rs,"sendUnauthorized");function ne(e){return{code:C(e),message:T(e),details:M(e)}}s(ne,"apiError");function se(e){return typeof e=="object"&&e!==null&&"status"in e&&typeof e.status=="number"?e.status:500}s(se,"errorStatus");function C(e){return typeof e=="object"&&e!==null&&"code"in e&&typeof e.code=="string"?e.code:"INTERNAL_ERROR"}s(C,"errorCode");function T(e){return e instanceof Error?e.message:String(e)}s(T,"errorMessage");function M(e){if(typeof e=="object"&&e!==null&&"details"in e)return e.details}s(M,"errorDetails");import Er from"crypto";function Ze(e,t){_r(e,t),t.setHeader("access-control-allow-methods","GET,POST,PATCH,DELETE,OPTIONS"),t.setHeader("access-control-allow-headers","content-type, authorization")}s(Ze,"setCors");function _r(e,t){let r=Fr();if(!r){t.setHeader("access-control-allow-origin","*");return}let o=e.headers.origin,n=Array.isArray(o)?o[0]:o;n&&r.includes(n)&&(t.setHeader("access-control-allow-origin",n),t.setHeader("vary","Origin"))}s(_r,"applyCorsOrigin");function Fr(){let e=process.env.AITABLE_WORKFLOW_ALLOWED_ORIGINS;if(typeof e!="string")return;let t=e.split(",").map(r=>r.trim()).filter(Boolean);return t.length?t:void 0}s(Fr,"readAllowedOrigins");function Wr(){let e=process.env.AITABLE_WORKFLOW_API_TOKEN,t=typeof e=="string"?e.trim():"";return t||void 0}s(Wr,"readServerApiToken");function Xe(e){let t=Wr();if(!t)return!0;let r=e.headers.authorization,o=Array.isArray(r)?r[0]:r;if(!o)return!1;let i=/^Bearer\s+(.+)$/i.exec(o.trim())?.[1]?.trim();return i?Cr(i,t):!1}s(Xe,"isRequestAuthorized");function Cr(e,t){let r=Buffer.from(e),o=Buffer.from(t);return r.length!==o.length?!1:Er.timingSafeEqual(r,o)}s(Cr,"timingSafeEqualString");function et(e){return Array.isArray(e)?e[0]:e}s(et,"headerValue");function tt(e,t){let r=t.toUpperCase();return!!(r==="OPTIONS"||r==="GET"&&e==="/api/health"||r==="GET"&&e==="/check.node"||r==="POST"&&e.startsWith("/api/auth/"))}s(tt,"isAuthExempt");function w(e,t,r=200){return e.newResponse(`${JSON.stringify(t)}
4
- `,r,{"content-type":"application/json; charset=utf-8"})}s(w,"jsonBody");function F(e,t,r,o,n){return w(e,{error:{code:r,message:o,details:n}},t)}s(F,"errorBody");function rt(e){return e.newResponse(`${JSON.stringify({error:"unauthorized"})}
5
- `,401,{"content-type":"application/json; charset=utf-8"})}s(rt,"unauthorized");function L(e){return async t=>(await e(t),t.env.rawHandled=!0,t.body(null))}s(L,"raw");function K(e){return e.env.services.execution}s(K,"backendOf");async function We(e,t){e.env.project||(e.env.project=await e.env.services.projectStore.projects.resolve(e.req.param("projectId"),e.env.scope)),await t()}s(We,"resolveProjectMw");import{Hono as kn}from"hono";import V from"path";import{validateDesignArtifacts as vn,buildEnvSource as Pn,loadWorkflowTemplate as fr}from"aitable-workflow-core";import{logger as Nr}from"aitable-workflow-core";var ie=new Set,le=new Map;function Ce(e,t,r){e.write(`id: ${t}
1
+ var Ye=Object.defineProperty;var Yr=Object.getOwnPropertyDescriptor;var o=(e,t)=>Ye(e,"name",{value:t,configurable:!0});var Qr=(e,t)=>{for(var r in t)Ye(e,r,{get:t[r],enumerable:!0})};var l=(e,t,r,s)=>{for(var n=s>1?void 0:s?Yr(t,r):t,i=e.length-1,a;i>=0;i--)(a=e[i])&&(n=(s?a(t,r,n):a(n))||n);return s&&n&&Ye(t,r,n),n};function Re(e){let t=Array.isArray(e)?e[0]:e;return t!=null&&t!==""?t:null}o(Re,"firstQueryValue");function A(e){return e.state.scope??{}}o(A,"scopeOf");var Ce={};Qr(Ce,{Configuration:()=>te,CorsMiddleware:()=>X,ErrorMappingMiddleware:()=>ee,IdentityMiddleware:()=>G,ProjectsController:()=>T,ResolveProjectMiddleware:()=>K,ServerConfiguration:()=>te,StagesController:()=>I,StaticWebMiddleware:()=>V,SystemController:()=>C,WorkflowsController:()=>N});import{Configuration as hs,App as ys}from"@midwayjs/core";import{Middleware as ss}from"@midwayjs/core";import Zr from"crypto";function mt(e,t){Xr(e,t),t.setHeader("access-control-allow-methods","GET,POST,PATCH,DELETE,OPTIONS"),t.setHeader("access-control-allow-headers","content-type, authorization")}o(mt,"setCors");function Xr(e,t){let r=es();if(!r){t.setHeader("access-control-allow-origin","*");return}let s=e.headers.origin,n=Array.isArray(s)?s[0]:s;n&&r.includes(n)&&(t.setHeader("access-control-allow-origin",n),t.setHeader("vary","Origin"))}o(Xr,"applyCorsOrigin");function es(){let e=process.env.AITABLE_WORKFLOW_ALLOWED_ORIGINS;if(typeof e!="string")return;let t=e.split(",").map(r=>r.trim()).filter(Boolean);return t.length?t:void 0}o(es,"readAllowedOrigins");function ts(){let e=process.env.AITABLE_WORKFLOW_API_TOKEN,t=typeof e=="string"?e.trim():"";return t||void 0}o(ts,"readServerApiToken");function gt(e){let t=ts();if(!t)return!0;let r=e.headers.authorization,s=Array.isArray(r)?r[0]:r;if(!s)return!1;let i=/^Bearer\s+(.+)$/i.exec(s.trim())?.[1]?.trim();return i?rs(i,t):!1}o(gt,"isRequestAuthorized");function rs(e,t){let r=Buffer.from(e),s=Buffer.from(t);return r.length!==s.length?!1:Zr.timingSafeEqual(r,s)}o(rs,"timingSafeEqualString");function wt(e){return Array.isArray(e)?e[0]:e}o(wt,"headerValue");function ht(e,t){let r=t.toUpperCase();return!!(r==="OPTIONS"||r==="GET"&&e==="/api/health"||r==="GET"&&e==="/check.node"||r==="POST"&&e.startsWith("/api/auth/"))}o(ht,"isAuthExempt");var X=class{resolve(){return async(t,r)=>{if(mt(t.req,t.res),t.method.toUpperCase()==="OPTIONS"){t.respond=!1,t.res.writeHead(204),t.res.end();return}await r()}}static getName(){return"aitableCors"}};o(X,"CorsMiddleware"),X=l([ss()],X);import{Middleware as os}from"@midwayjs/core";async function W(e){let t=[];for await(let s of e)t.push(Buffer.isBuffer(s)?s:Buffer.from(s));let r=Buffer.concat(t).toString("utf8").trim();return r?JSON.parse(r):{}}o(W,"readJson");function m(e,t,r=200){e.writeHead(r,{"content-type":"application/json; charset=utf-8"}),e.end(`${JSON.stringify(t)}
2
+ `)}o(m,"sendJson");function yt(e,t,r=200){e.writeHead(r,{"content-type":"text/plain; charset=UTF-8"}),e.end(t)}o(yt,"sendText");function S(e,t,r,s,n){m(e,{error:{code:r,message:s,details:n}},t)}o(S,"sendError");function vt(e){e.writeHead(401,{"content-type":"application/json; charset=utf-8"}),e.end(`${JSON.stringify({error:"unauthorized"})}
3
+ `)}o(vt,"sendUnauthorized");function me(e){return{code:B(e),message:x(e),details:U(e)}}o(me,"apiError");function pe(e){return typeof e=="object"&&e!==null&&"status"in e&&typeof e.status=="number"?e.status:500}o(pe,"errorStatus");function B(e){return typeof e=="object"&&e!==null&&"code"in e&&typeof e.code=="string"?e.code:"INTERNAL_ERROR"}o(B,"errorCode");function x(e){return e instanceof Error?e.message:String(e)}o(x,"errorMessage");function U(e){if(typeof e=="object"&&e!==null&&"details"in e)return e.details}o(U,"errorDetails");function ns(e){if(pe(e)!==404||typeof e!="object"||e===null)return!1;let{code:t,name:r}=e;return t==="404"||t===404||r==="NotFoundError"}o(ns,"isFrameworkRouteMiss");var ee=class{resolve(){return async(t,r)=>{try{await r(),t.res.headersSent||(t.respond=!1,S(t.res,404,"NOT_FOUND","\u63A5\u53E3\u4E0D\u5B58\u5728"))}catch(s){if(t.res.headersSent){t.res.end();return}if(t.respond=!1,ns(s)){S(t.res,404,"NOT_FOUND","\u63A5\u53E3\u4E0D\u5B58\u5728");return}S(t.res,pe(s),B(s),x(s),U(s))}}}static getName(){return"aitableErrorMapping"}};o(ee,"ErrorMappingMiddleware"),ee=l([os()],ee);import{Middleware as is,Inject as as}from"@midwayjs/core";var G=class{serverServices;resolve(){return async(t,r)=>{let{identityService:s}=this.serverServices;t.state.scope=await s.resolveScope(t.req);let n=t.path,i=t.method;if(!(ht(n,i)||i==="GET"&&n==="/api/public-config")&&!await s.authenticate(t.req)){t.respond=!1,vt(t.res);return}await r()}}static getName(){return"aitableIdentity"}};o(G,"IdentityMiddleware"),l([as()],G.prototype,"serverServices",2),G=l([is()],G);import{Middleware as cs,Inject as us}from"@midwayjs/core";function ps(e){let r=/^\/api\/projects\/([^/?#]+)/.exec(e)?.[1];if(r)try{return decodeURIComponent(r)}catch{return r}}o(ps,"projectIdFromPath");var K=class{serverServices;resolve(){return async(t,r)=>{let s=ps(t.path);s&&!t.state.project&&(t.state.project=await this.serverServices.projectStore.projects.resolve(s,A(t))),await r()}}static getName(){return"aitableResolveProject"}};o(K,"ResolveProjectMiddleware"),l([us()],K.prototype,"serverServices",2),K=l([cs()],K);import{Middleware as gs,Inject as ws}from"@midwayjs/core";import Qe from"path";import{readFile as ds,access as ls}from"fs/promises";import{resolveInsideRoot as fs}from"aitable-workflow-core";var ms={".html":"text/html; charset=utf-8",".js":"text/javascript; charset=utf-8",".mjs":"text/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".json":"application/json; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".ico":"image/x-icon",".webp":"image/webp",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".map":"application/json; charset=utf-8",".txt":"text/plain; charset=utf-8"};async function Pt(e,t,r,s){let n=Qe.resolve(e),i=Qe.join(n,"index.html"),a=decodeURIComponent(t).replace(/^\/+/,""),c;try{c=a===""?i:fs(n,a,{allowRoot:!1})}catch{c=i}try{await ls(c)}catch{c=i}try{let u=await ds(c),p=Qe.extname(c).toLowerCase(),f=ms[p]??"application/octet-stream";if(s.writeHead(200,{"Content-Type":f,"Content-Length":u.byteLength}),r.method==="HEAD"){s.end();return}s.end(u)}catch{S(s,404,"WEB_NOT_BUILT","\u672C\u5730 web \u8D44\u6E90\u4E0D\u5B58\u5728\uFF08\u8BF7\u5148\u6784\u5EFA\u524D\u7AEF\u6216\u68C0\u67E5 --web-root\uFF09")}}o(Pt,"serveStaticWeb");var V=class{serverServices;resolve(){return async(t,r)=>{let s=this.serverServices.webRoot,n=t.method.toUpperCase(),i=t.path;if(s&&(n==="GET"||n==="HEAD")&&!i.startsWith("/api/")&&i!=="/check.node"){t.respond=!1,await Pt(s,i,t.req,t.res);return}await r()}}static getName(){return"aitableStaticWeb"}};o(V,"StaticWebMiddleware"),l([ws()],V.prototype,"serverServices",2),V=l([gs()],V);var te=class{app;async onReady(){this.app.useMiddleware([X,ee,V,G,K])}};o(te,"ServerConfiguration"),l([ys()],te.prototype,"app",2),te=l([hs({namespace:"aitableServer"})],te);import{Controller as vs,Get as be,Inject as kt}from"@midwayjs/core";import{detectAgents as Ps}from"aitable-workflow-core";var C=class{ctx;serverServices;async health(){this.ctx.respond=!1,m(this.ctx.res,{ok:!0})}async checkNode(){this.ctx.respond=!1,yt(this.ctx.res,"success")}async publicConfig(){let t=process.env.RELAY_PUBLIC_GATEWAY_URL?.trim();this.ctx.respond=!1,m(this.ctx.res,{authEnabled:this.serverServices.identityService.authRequired,...t?{relayGatewayUrl:t}:{}})}async agents(){this.ctx.respond=!1,m(this.ctx.res,Ps())}};o(C,"SystemController"),l([kt()],C.prototype,"ctx",2),l([kt()],C.prototype,"serverServices",2),l([be("/api/health")],C.prototype,"health",1),l([be("/check.node")],C.prototype,"checkNode",1),l([be("/api/public-config")],C.prototype,"publicConfig",1),l([be("/api/agents")],C.prototype,"agents",1),C=l([vs("/")],C);import re from"path";import{Controller as mo,Del as go,Get as he,Inject as wr,Patch as it,Post as Le,Put as hr}from"@midwayjs/core";import{validateDesignArtifacts as wo,buildEnvSource as ho,loadWorkflowTemplate as yr}from"aitable-workflow-core";import{logger as ks}from"aitable-workflow-core";var ge=new Set,Te=new Map;function Ze(e,t,r){e.write(`id: ${t}
6
4
  `),e.write(`event: ${r.type}
7
5
  `),e.write(`data: ${JSON.stringify(r)}
8
6
 
9
- `)}s(Ce,"writeDesignSse");function ot(e,t){let r=e.buffer.push(t);for(let o of e.subscribers){if(o.destroyed||o.writableEnded){e.subscribers.delete(o);continue}try{Ce(o,r.seq,t)}catch{e.subscribers.delete(o)}}}s(ot,"publishDesignEvent");function nt(e){ie.delete(e);let t=le.get(e);if(le.delete(e),!!t){for(let r of t.subscribers)if(!r.writableEnded)try{r.end()}catch{}t.subscribers.clear()}}s(nt,"closeDesignSession");function st(e,t){let r=e.searchParams.get("cursor")??et(t.headers["last-event-id"]);if(r==null||r==="")return;let o=Number(r);return Number.isFinite(o)?o:void 0}s(st,"parseReconnectCursor");function Mr(e,t){return e.runtime.runSessions.has(t)||ie.has(t)}s(Mr,"isStageActiveFor");function O(e,t){return t.projectStore.prepareWorkdir(e,{isStageActive:s(()=>Mr(t,e.id),"isStageActive")})}s(O,"prepareWorkdirGuarded");async function fe(e,t,r){try{let n=(await e.projectStore.instances.getState(t)).stages[r]?.task;if(!n)return;let i=n.status;if(i!=="succeeded"&&i!=="failed"&&i!=="cancelled")return;await e.projectStore.commitWorkdir(t,{stage:r})}catch(o){Nr.warn("stage-sync","stage \u6D41\u56DE\u5199\u6743\u5A01\u6E90\u5931\u8D25",{projectId:t.id,stage:r,error:T(o)})}}s(fe,"commitStageOnTerminal");async function it(e,t){await e.projectStore.cascadeDelete(t).catch(()=>{}),await e.execution.unbindProjectMachine(t).catch(()=>{})}s(it,"cascadeDeleteProject");import ve from"path";import{readFile as jt,writeFile as Kr,access as Gr,readdir as Vr}from"fs/promises";import He from"yaml";import{scaffold as Yr,writeProfileRoles as Qr,isRecord as Pe}from"aitable-workflow-core";import{createHash as Lr,randomUUID as Br}from"crypto";import{mkdir as Ne,readdir as at,readFile as Me,rm as ct,stat as ut,writeFile as pt}from"fs/promises";import S from"path";var $r=S.resolve(process.cwd(),".sandbox","aitable-workflow-server"),dt=/^[a-zA-Z0-9_-]+$/,Hr=/\.(json|ya?ml|ts|js|md|txt)$/i,qr=["workflow.yml","views.yml","recipes","traces"];function lt(e={}){let t=S.resolve(e.dataRoot??process.env.AITABLE_WORKFLOW_DATA_DIR??$r);return{dataRoot:t,projectsRoot:S.join(t,"projects")}}s(lt,"createWorkspace");async function ft(e){await Ne(e.projectsRoot,{recursive:!0});let t=new Date().toISOString(),r=(await Le(e)).length+1,o={id:`wf_${Br().slice(0,8)}`,title:`\u5DE5\u4F5C\u6D41 #${r}`,dir:"",createdAt:t,updatedAt:t};return o.dir=ht(e,o.id),await Ne(o.dir,{recursive:!0}),await ke(o),o}s(ft,"createProject");async function ae(e,t){let r=await R({dataRoot:S.dirname(S.dirname(e.dir)),projectsRoot:S.dirname(e.dir)},e.id),o={...r,...t,title:t.title?.trim()||r.title,updatedAt:new Date().toISOString()};return await ke(o),o}s(ae,"updateProjectMeta");async function Le(e){let t=await at(e.projectsRoot,{withFileTypes:!0}).catch(()=>[]);return(await Promise.all(t.filter(o=>o.isDirectory()&&dt.test(o.name)).map(o=>R(e,o.name).catch(()=>null)))).filter(o=>o!==null).sort((o,n)=>n.updatedAt.localeCompare(o.updatedAt))}s(Le,"listProjects");async function R(e,t){kt(t);let r=ht(e,t);if(!(await ut(r).catch(()=>null))?.isDirectory())throw Object.assign(new Error(`\u9879\u76EE\u4E0D\u5B58\u5728: ${t}`),{code:"PROJECT_NOT_FOUND",status:404});let n=S.join(r,"project.json"),i=await Me(n,"utf8").catch(()=>"");if(!i){let c=new Date().toISOString(),u={id:t,dir:r,createdAt:c,updatedAt:c};return await ke(u),u}return{...JSON.parse(i),dir:r}}s(R,"resolveProject");async function x(e){let t={...e,updatedAt:new Date().toISOString()};return await ke(t),t}s(x,"touchProject");async function mt(e){return await Promise.all(qr.map(t=>ct(S.join(e.dir,t),{recursive:!0,force:!0}))),await x(e)}s(mt,"resetProjectWorkflow");async function he(e){await ct(e.dir,{recursive:!0,force:!0})}s(he,"deleteProject");async function Ur(e){let t=await Me(yt(e),"utf8").catch(()=>"");return t.trim()?JSON.parse(t):null}s(Ur,"readDesignManifest");async function Be(e,t){await Ne(wt(e),{recursive:!0}),await pt(yt(e),`${JSON.stringify(t,null,2)}
10
- `,"utf8")}s(Be,"writeDesignManifest");async function gt(e){let t=await Ur(e);if(!t)return{status:"missing_manifest",manifest:null,errors:["\u7F3A\u5C11 Design \u4EA7\u7269\u6E05\u5355"],missing:["runtime/artifacts/design-manifest.json"]};if(t.status!=="ready"||!t.validation.passed)return{status:"invalid",manifest:t,errors:t.validation.errors.length?t.validation.errors:["Design \u4EA7\u7269\u6E05\u5355\u672A\u5C31\u7EEA"],missing:[]};let r=t.generated.filter(n=>n.required).map(n=>n.path),o=[];for(let n of r)(await ut(S.join(e.dir,n)).catch(()=>null))?.isFile()||o.push(n);return o.length>0?{status:"artifact_missing",manifest:t,errors:[`\u7F3A\u5C11 Design \u4EA7\u7269\uFF1A${o.join(", ")}`],missing:o}:{status:"ready",manifest:t,errors:[],missing:[]}}s(gt,"validateDesignArtifacts");function me(e){return Lr("sha256").update(e).digest("hex")}s(me,"checksumContent");async function D(e){let t=[];return await vt(e.dir,e.dir,t),t.sort((r,o)=>r.path.localeCompare(o.path))}s(D,"listProjectFiles");function B(e){return S.join(e.dir,"aitable-workflow.config.yml")}s(B,"configPath");function $e(e,t,r){let o=new URLSearchParams({entrance:"data",sheetId:t});return r?.trim()&&o.set("viewId",r.trim()),`https://alidocs.dingtalk.com/i/nodes/${e}?iframeQuery=${encodeURIComponent(o.toString())}`}s($e,"buildAITableUrl");function zr(e){return S.join(e.dir,"runtime")}s(zr,"runtimeDir");function wt(e){return S.join(zr(e),"artifacts")}s(wt,"artifactsDir");function yt(e){return S.join(wt(e),"design-manifest.json")}s(yt,"designManifestPath");function ht(e,t){return kt(t),S.join(e.projectsRoot,t)}s(ht,"projectDir");function kt(e){if(!dt.test(e))throw Object.assign(new Error(`\u975E\u6CD5\u9879\u76EE ID: ${e}`),{code:"INVALID_PROJECT_ID"})}s(kt,"assertProjectId");async function ke(e){await pt(S.join(e.dir,"project.json"),`${JSON.stringify(e,null,2)}
11
- `,"utf8")}s(ke,"writeProjectMeta");async function vt(e,t,r){let o=await at(t,{withFileTypes:!0}).catch(()=>[]);for(let n of o){if(Jr(e,S.join(t,n.name),n.name))continue;let i=S.join(t,n.name);if(n.isDirectory()){await vt(e,i,r);continue}if(!n.isFile()||!Hr.test(n.name))continue;let a=S.relative(e,i).split(S.sep).join("/"),c=await Me(i,"utf8").catch(()=>"");r.push({path:a,content:c})}}s(vt,"walk");function Jr(e,t,r){return r==="node_modules"||r==="traces"||r==="runtime"||r===".aitable"||r==="project.json"?!0:S.relative(e,t).split(S.sep).join("/").startsWith(".aitable/")}s(Jr,"isHiddenProjectFile");function Pt(e,t=1){let r=e.replace(/\s+/g," ").trim();if(!r)return`\u5DE5\u4F5C\u6D41 #${t}`;let o=r.split(/[。.!!?\n]/u)[0]?.split(/[::,,;;]/u)[0]?.trim();return o?o.length>16?o.slice(0,16):o:`\u5DE5\u4F5C\u6D41 #${t}`}s(Pt,"deriveProjectTitleFromSop");async function Tt(e,t,r,o,n,i){let a=await R(t,e),c=await I(o),u=await r.createTask(a,"init");try{let p=Xr(c),l=await Yr({answers:p,cwd:a.dir,force:!0}),f=await Qr(ve.join(a.dir,"profile","profile.yml"),c.roles??[]);c.machineId&&i&&await i(e,c.machineId).catch(()=>{});let h=await x(a),k=f.skippedKnowledgeBindings.map(y=>`\u89D2\u8272 ${y.roleId} \u7684\u77E5\u8BC6\u5E93\u7ED1\u5B9A\u672A\u5199\u5165 profile\uFF0C\u540E\u7EED\u5E94\u4E0B\u6C89\u5230\u90E8\u7F72 Binding\uFF1A${y.knowledge.join(", ")}`);await r.updateTask(a,u.id,{status:"succeeded",checkpoint:"done",currentAction:"\u914D\u7F6E\u5DF2\u5B8C\u6210",outputs:{warnings:k,roleFiles:[]}});let d=Pt(c.sop,1),v=await ae(h,{title:d});_(n,{project:v,scaffold:l,roleFiles:[],files:await D(h),warnings:k})}catch(p){throw await r.updateTask(a,u.id,{status:"failed",checkpoint:"init_failed",currentAction:"\u914D\u7F6E\u751F\u6210\u5931\u8D25",error:{code:C(p),message:T(p),retryable:!0,details:M(p)}}),p}}s(Tt,"handleInit");async function St(e,t,r,o,n){let i=await R(t,e),c=(await I(o)).tableId?.trim()??"";if(!c)throw Object.assign(new Error("Table Id \u4E0D\u80FD\u4E3A\u7A7A"),{code:"TABLE_ID_REQUIRED",status:422});let u=B(i),p=await jt(u,"utf8"),l=He.parse(p);if(!Pe(l)||!Pe(l.tracker))throw Object.assign(new Error("\u914D\u7F6E\u6587\u4EF6\u7F3A\u5C11 tracker\uFF0C\u65E0\u6CD5\u5199\u56DE Table Id"),{code:"CONFIG_TRACKER_MISSING",status:500});l.tracker.table_id=c,await Kr(u,He.stringify(l),"utf8");let f=await x(i);_(n,{project:f,files:await D(f),state:await r.getRecoverableState(f)})}s(St,"handleUpdateProjectTableId");async function Dt(e,t,r,o,n,i,a){let c=await R(t,e),u=await I(i);if(u.action!=="confirm"&&u.action!=="terminate")throw Object.assign(new Error("\u4EBA\u5DE5\u64CD\u4F5C\u53EA\u652F\u6301 confirm \u6216 terminate"),{code:"INVALID_HUMAN_ACTION",status:422});let p=o.runSessions.get(e)?.app;if(!p)throw Object.assign(new Error("\u8FD0\u884C\u670D\u52A1\u672A\u542F\u52A8\uFF0C\u65E0\u6CD5\u5904\u7406\u4EBA\u5DE5\u786E\u8BA4\u64CD\u4F5C"),{code:"RUN_SESSION_REQUIRED",status:409});let l=u.action==="confirm"?p.settings.tracker.states.active[0]:p.settings.tracker.states.terminal.find(d=>d==="\u5DF2\u5173\u95ED")??p.settings.tracker.states.terminal.at(-1);if(!l)throw Object.assign(new Error("\u672A\u914D\u7F6E\u53EF\u7528\u4E8E\u4EBA\u5DE5\u64CD\u4F5C\u7684\u76EE\u6807\u72B6\u6001"),{code:"HUMAN_ACTION_STATE_MISSING",status:500});let f=u.action==="confirm"?"\u4EBA\u5DE5\u786E\u8BA4\uFF1A\u7EE7\u7EED\u6D41\u7A0B":"\u4EBA\u5DE5\u786E\u8BA4\uFF1A\u7EC8\u6B62\u6D41\u7A0B",h=null;u.action==="confirm"&&(h=await Zr(p,n)),await p.tracker.updateState(n,l),h&&await p.tracker.updateCurrentStep(n,h),await p.tracker.appendEvent(n,f);let k=await r.updateRunSnapshotRecord(c,n,u.action==="confirm"?{state:l,status:"running",currentStepId:h??void 0}:{state:l,status:"done",currentStepId:null},f);_(a,{action:u.action,recordId:n,state:k})}s(Dt,"handleRunHumanAction");async function Zr(e,t){let[r]=await e.tracker.fetchItemsByIds([t]).catch(()=>[]),o=r?.currentStep;if(!o)return null;let n=e.workflow.orderedStepIds.indexOf(o);return n<0?null:e.workflow.orderedStepIds[n+1]??null}s(Zr,"resolveNextStepId");function Xr(e){let t=e.tableBaseId.trim();if(!t)throw Object.assign(new Error("Base Id \u4E0D\u80FD\u4E3A\u7A7A"),{code:"BASE_ID_REQUIRED",status:422});let r=e.llmProvider.trim();return{agentType:to(e.agentsSelected),scene:eo(e),baseId:t,tableId:e.tableId?.trim()??"",llmProvider:r,llmModel:r?e.llmModel.trim()||ro(r):"",llmApiKeyEnv:r?oo(r):"",confirmed:!0}}s(Xr,"toInitAnswers");function eo(e){return e.sop.trim()||"\u7528\u6237\u5728 AI \u8868\u683C\u4E2D\u63D0\u4EA4\u4E1A\u52A1\u8BB0\u5F55\uFF0C\u7CFB\u7EDF\u81EA\u52A8\u5B8C\u6210\u5206\u7C7B\u3001\u5904\u7406\u3001\u5BA1\u6838\u4E0E\u56DE\u5199\u3002"}s(eo,"buildScene");function to(e){return e.includes("qwenwork")?"qwenwork":e.includes("qoder")?"qoder":e.includes("codex")?"codex":"claudecode"}s(to,"toAgentType");function ro(e){return e==="openai"?"gpt-4o":e==="anthropic"?"claude-sonnet-4-20250514":e==="dashscope"?"qwen-max":"Qwen3.7-Max-DogFooding"}s(ro,"defaultModel");function oo(e){return e==="openai"?"OPENAI_API_KEY":e==="anthropic"?"ANTHROPIC_API_KEY":e==="dashscope"?"DASHSCOPE_API_KEY":e==="idealab"?"IDEALAB_API_KEY":"LLM_API_KEY"}s(oo,"defaultApiKeyEnv");async function Ot(e,t){let r=ve.join(e.dir,"aitable-workflow.config.yml");try{await Gr(r)}catch{t.warnings.push("aitable-workflow.config.yml \u4E0D\u5B58\u5728\uFF08\u9879\u76EE\u7EA7\u914D\u7F6E\u7F3A\u5931\uFF0C\u8FD0\u884C\u524D\u9700\u8981\u751F\u6210\uFF09")}await no(e.dir)||t.warnings.push("\u672A\u627E\u5230\u4EFB\u4F55\u89D2\u8272\u5B9A\u4E49\uFF08profile/profile.yml \u7684 roles \u6BB5\u6216 roles/*.role.yml\uFF0Cdesign \u9636\u6BB5 requiresAgent \u7684 step \u9700\u7ED1\u5B9A role\uFF09")}s(Ot,"augmentProjectFileValidation");async function no(e){try{let r=He.parse(await jt(ve.join(e,"profile","profile.yml"),"utf8"));if(Pe(r)&&Pe(r.roles)&&Object.keys(r.roles).length>0)return!0}catch{}return(await Vr(ve.join(e,"roles")).catch(()=>[])).some(r=>r.endsWith(".role.yml")||r.endsWith(".role.yaml"))}s(no,"hasAnyRoleDefinition");import Rt from"fs";import so from"path";var io=/requiresAgent\s*:\s*(true|false)/,At=new Map;function bt(e,t){if(!t)return!1;for(let r of[".recipe.ts",".recipe.js"]){let o=so.join(e,`${t}${r}`);try{let n=Rt.statSync(o);if(!n.isFile())continue;let i=`${n.mtimeMs}:${n.size}`,a=At.get(o);if(a?.signature===i)return a.requiresAgent;let u=Rt.readFileSync(o,"utf8").match(io),p=u?u[1]==="true":!1;return At.set(o,{signature:i,requiresAgent:p}),p}catch(n){if(n.code==="ENOENT")continue;return!1}}return!1}s(bt,"readRecipeRequiresAgent");import{rm as xt,mkdir as ao,writeFile as co}from"fs/promises";import ge from"path";import uo from"yaml";import{isRecord as po,resolveInsideRoot as lo}from"aitable-workflow-core";var It=/\.(json|ya?ml|ts|js|md|txt)$/i,fo=new Set(["runtime","traces","node_modules"]),Et="aitable-workflow.config.yml";function _t(e,t){try{lo(e,t,{allowRoot:!1})}catch{throw Object.assign(new Error(`\u8DEF\u5F84\u8D8A\u51FA\u9879\u76EE\u76EE\u5F55: ${t}`),{code:"invalid_path",status:400})}}s(_t,"assertSafeRelativePath");function Ft(e,t){_t(e.dir,t)}s(Ft,"assertSafeProjectPath");function qs(e){_t("/workflow-root",e)}s(qs,"assertSafeWorkflowPath");function Wt(e){let r=Nt(e).split("/")[0]??"";return fo.has(r)}s(Wt,"isForbiddenWritable");function mo(e){if(Wt(e))throw Object.assign(new Error(`\u7981\u6B62\u5199\u5165\u53D7\u4FDD\u62A4\u76EE\u5F55: ${e}`),{code:"invalid_path",status:400});if(!It.test(e))throw Object.assign(new Error(`\u6587\u4EF6\u7C7B\u578B\u4E0D\u5141\u8BB8\u5199\u5165: ${e}\uFF08\u4EC5\u5141\u8BB8 ${It.source}\uFF09`),{code:"invalid_path",status:400})}s(mo,"assertWritableKind");function Ct(e){let t=Nt(e);return t===Et||t.endsWith(".config.yml")?"config":t==="workflow.yml"?"workflow":t==="views.yml"?"views":t.startsWith("recipes/")?"recipe":t.startsWith("roles/")?"role":"other"}s(Ct,"kindOf");function Nt(e){return ge.posix.normalize(e.replace(/\\/g,"/")).replace(/^\.\//,"")}s(Nt,"normalizeLogicalPath");function go(e){let t=[],r;try{r=uo.parse(e)}catch(n){return t.push(`aitable-workflow.config.yml \u89E3\u6790\u5931\u8D25: ${n instanceof Error?n.message:String(n)}`),t}if(!po(r))return t.push("aitable-workflow.config.yml \u9876\u5C42\u5E94\u4E3A\u5BF9\u8C61"),t;let o=r.scene;return(typeof o!="string"||o.trim()==="")&&t.push("aitable-workflow.config.yml \u7F3A\u5C11 scene \u5B57\u6BB5\uFF08\u5EFA\u8BAE\u586B\u5199\u4E1A\u52A1\u573A\u666F\u63CF\u8FF0\uFF09"),t}s(go,"validateConfigScene");async function je(e,t){let r=[],o=[];for(let n of t){Ft(e,n.path),mo(n.path);let i=ge.join(e.dir,n.path);await ao(ge.dirname(i),{recursive:!0}),await co(i,n.content,"utf8"),r.push({path:n.path,content:n.content}),n.path===Et&&o.push(...go(n.content))}return{written:r,warnings:o}}s(je,"writeProjectFiles");async function Mt(e,t){for(let r of t){if(Ft(e,r),Wt(r))throw Object.assign(new Error(`\u7981\u6B62\u5220\u9664\u53D7\u4FDD\u62A4\u76EE\u5F55: ${r}`),{code:"invalid_path",status:400});await xt(ge.join(e.dir,r),{force:!0})}}s(Mt,"removeProjectFiles");async function Lt(e,t){let r=await wo(e);for(let o of r)await xt(ge.join(e.dir,o),{force:!0}).catch(()=>{});return je(e,t)}s(Lt,"replaceProjectFiles");async function wo(e){return(await D(e)).map(r=>r.path).filter(r=>Ct(r)!=="other")}s(wo,"collectBusinessFiles");function yo(e){return me(e.content)}s(yo,"checksumFile");async function Bt(e){let t=await D(e),r=t.map(o=>({path:o.path,sha256:yo(o)}));return{project:e,files:t,checksums:r,exportedAt:new Date().toISOString()}}s(Bt,"buildProjectExport");var ho="coding-agents.config.json";function $t(e){let t=[],r=e.files,o=e.checksums,n=r.findIndex(i=>i.path===ho);if(n>=0){let i=r[n],a=i.content;try{let c=JSON.parse(i.content);if(c&&typeof c=="object"&&!Array.isArray(c)){let u=c.agents;if(u&&typeof u=="object"&&!Array.isArray(u)){let p=!1;for(let l of Object.keys(u)){let f=u[l];if(!f||typeof f!="object"||Array.isArray(f))continue;let h=f.env;if(h&&typeof h=="object"&&!Array.isArray(h)){let k=h;if(Object.values(k).some(v=>typeof v=="string"&&v!=="")){for(let v of Object.keys(k))k[v]="";p=!0}}}p&&(a=JSON.stringify(c,null,2)+`
12
- `)}}}catch{}if(a!==i.content){let c={path:i.path,content:a};r=r.map((p,l)=>l===n?c:p);let u=me(a);o=o.map((p,l)=>l===n?{path:p.path,sha256:u}:p),t.push(i.path)}}return{...e,files:r,checksums:o,redacted:t.length>0?t:e.redacted}}s($t,"redactExportBundle");import{logger as Ht}from"aitable-workflow-core";function Te(e,t){let r={...e.stages,[t.stage]:we(t)};return U({id:t.projectId,dir:"",createdAt:"",updatedAt:t.updatedAt},{...e,projectId:t.projectId,activeStage:qe(t.status)?t.stage:e.activeStage===t.stage?null:e.activeStage,stages:r,updatedAt:t.updatedAt})}s(Te,"mergeTaskIntoState");function U(e,t){let r=new Date().toISOString(),o=t.stages??{};return{projectId:t.projectId??e.id,activeStage:t.activeStage??ko(o),stages:o,runSnapshot:t.runSnapshot,updatedAt:t.updatedAt??e.updatedAt??r}}s(U,"normalizeProjectState");function we(e){let t=vo(e);return t?{task:e,recoveryAction:t}:{task:e}}s(we,"stageStateOf");function N(e){if(!e.activeStage)return null;let t=e.stages[e.activeStage]?.task;return t&&qe(t.status)?t:null}s(N,"activeTaskOf");function qe(e){return e==="running"||e==="blocked"}s(qe,"isActiveStatus");function Se(e){return e==="failed"||e==="succeeded"||e==="cancelled"}s(Se,"isTerminalStatus");function ko(e){for(let[t,r]of Object.entries(e))if(r&&qe(r.task.status))return t;return null}s(ko,"findActiveStage");function vo(e){if(e.status==="blocked"&&e.stage==="design"&&e.block?.reason==="discovery_required")return{type:"confirm_input",label:e.block.actionLabel,description:e.block.message,api:{method:"POST",path:`/api/projects/${e.projectId}/design/discovery/confirm`}};if(e.status==="blocked")return{type:"continue",label:e.block?.actionLabel??Po(e.stage),description:e.block?.message??e.currentAction,api:{method:"POST",path:`/api/projects/${e.projectId}/tasks/${e.stage}/continue`}};if(e.status==="failed"&&e.error?.retryable)return{type:"retry",label:jo(e.stage),description:e.error.message,api:{method:"POST",path:To(e)}}}s(vo,"recoveryActionForTask");function Po(e){return e==="setup"?"\u7EE7\u7EED\u642D\u5EFA":e==="design"?"\u7EE7\u7EED\u8BBE\u8BA1":e==="run"?"\u7EE7\u7EED\u8FD0\u884C":"\u7EE7\u7EED"}s(Po,"continueLabel");function jo(e){return e==="design"?"\u91CD\u65B0\u751F\u6210\u5DE5\u4F5C\u6D41\u8BBE\u8BA1":e==="setup"?"\u91CD\u8BD5\u642D\u5EFA":e==="run"?"\u91CD\u8BD5\u8FD0\u884C":"\u91CD\u8BD5"}s(jo,"retryLabel");function To(e){return e.stage==="design"?`/api/projects/${e.projectId}/design`:`/api/projects/${e.projectId}/tasks/${e.stage}/retry`}s(To,"retryPath");function Ut(e){return{appFactory:e,runSessions:new Map}}s(Ut,"createServerRuntime");async function zt(e,t,r,o,n,i){let a=await R(t,e),c=await r.getState(a),u=o.runSessions.get(e);if(u){qt(u,n,i);let h=await r.getState(a),k=N(h),d=k?.stage==="run"?k:h.stages.run?.task;d&&Kt(u,{type:"task.snapshot",task:d},i);return}let p=N(c);if(p?.stage==="run"&&p.status==="running"){let k={type:"task.failed",task:await r.updateTask(a,p.id,{status:"failed",checkpoint:"session_lost",currentAction:"\u8FD0\u884C\u670D\u52A1\u8FDE\u63A5\u5DF2\u65AD\u5F00",error:{code:"RUN_SESSION_LOST",message:"\u540E\u7AEF\u8FD0\u884C\u4F1A\u8BDD\u5DF2\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u662F\u670D\u52A1\u91CD\u542F\u3002\u8BF7\u91CD\u8BD5\u8FD0\u884C\u3002",retryable:!0}})};i(n,k.type,k),i(n,"error",{type:"error",error:{code:"RUN_SESSION_LOST",message:"\u540E\u7AEF\u8FD0\u884C\u4F1A\u8BDD\u5DF2\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u662F\u670D\u52A1\u91CD\u542F\u3002\u8BF7\u91CD\u8BD5\u8FD0\u884C\u3002"}}),n.end();return}let l=await r.createTask(a,"run"),f={taskId:l.id,subscribers:new Set,events:[],app:null,snapshotWrite:Promise.resolve()};o.runSessions.set(e,f),qt(f,n,i),So(a,l,f,o,r,i)}s(zt,"handleRunRuntime");function Jt(e,t){if(!t)return e;let r=e.stages.run?.task;return!r||r.status==="running"?e:{...e,activeStage:"run",stages:{...e.stages,run:{task:{...r,id:t.taskId,status:"running",checkpoint:"listening",currentAction:"\u771F\u5B9E\u8FD0\u884C\u670D\u52A1\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u76D1\u542C AI \u8868\u683C\u4E2D\u7684\u5F85\u5904\u7406\u8BB0\u5F55",block:void 0,error:void 0}}}}}s(Jt,"overlayRunSessionState");async function So(e,t,r,o,n,i){let a=s(u=>{Kt(r,u,i),Oo(u)&&(Ht.info("run-runtime","publish run execution event",Ro(u)),r.snapshotWrite=r.snapshotWrite.then(()=>n.appendRunSnapshotEvent(e,u)).catch(p=>{Ht.error("run-runtime","\u5199\u5165\u8FD0\u884C\u5FEB\u7167\u5931\u8D25",{projectId:e.id,event:u.type,error:p instanceof Error?p.message:String(p)})}))},"send"),c=s(async(u,p)=>{let l=await n.updateTask(e,t.id,{currentAction:u,checkpoint:p});a({type:"task.progress",taskId:l.id,message:u,checkpoint:p}),a({type:"run.progress",message:u})},"progress");try{a({type:"task.started",task:t}),await c("\u52A0\u8F7D\u771F\u5B9E\u8FD0\u884C\u914D\u7F6E","loading");let u=await o.appFactory(B(e),l=>a(Do(l)));r.app=u,await c("\u771F\u5B9E\u8FD0\u884C\u670D\u52A1\u5DF2\u8FDE\u63A5","started"),a({type:"run.started",projectId:e.id,workflowName:u.workflow.meta.name,stepCount:u.workflow.orderedStepIds.length,intervalMs:u.settings.polling.interval_ms}),await c("\u771F\u5B9E\u8FD0\u884C\u670D\u52A1\u5DF2\u542F\u52A8\uFF0C\u5F00\u59CB\u76D1\u542C AI \u8868\u683C\u4E2D\u7684\u5F85\u5904\u7406\u8BB0\u5F55","listening"),await u.start();let p=await n.updateTask(e,t.id,{status:"succeeded",checkpoint:"stopped",currentAction:"\u771F\u5B9E\u8FD0\u884C\u670D\u52A1\u5DF2\u505C\u6B62"});a({type:"task.succeeded",task:p}),a({type:"run.stopped",message:"\u771F\u5B9E\u8FD0\u884C\u670D\u52A1\u5DF2\u505C\u6B62"})}catch(u){let p=await n.updateTask(e,t.id,{status:"failed",checkpoint:"failed",currentAction:"\u771F\u5B9E\u8FD0\u884C\u542F\u52A8\u5931\u8D25",error:{code:Gt(u),message:Vt(u),retryable:!0,details:Yt(u)}});a({type:"task.failed",task:p}),a({type:"error",error:bo(u)})}finally{o.runSessions.delete(e.id),Ao(r)}}s(So,"startRunSession");function Do(e){switch(e.type){case"record.observed":return{type:"run.record.detected",record:e.record};case"step.started":return{type:"run.step.started",itemId:e.itemId,stepId:e.stepId,stepName:e.stepName};case"step.succeeded":return{type:"run.step.succeeded",itemId:e.itemId,stepId:e.stepId,stepName:e.stepName,logs:e.logs};case"step.failed":return{type:"run.step.failed",itemId:e.itemId,stepId:e.stepId,stepName:e.stepName,error:e.error,logs:e.logs};case"record.blocked":return{type:"run.record.blocked",itemId:e.itemId,stepId:e.stepId,stepName:e.stepName,reason:e.reason,logs:e.logs};case"record.completed":return{type:"run.record.completed",itemId:e.itemId}}}s(Do,"toRunEventDTO");function Oo(e){return e.type.startsWith("run.record.")||e.type.startsWith("run.step.")}s(Oo,"isRunExecutionEvent");function Ro(e){return e.type==="run.record.detected"?{type:e.type,itemId:e.record.id,state:e.record.state,currentStep:e.record.currentStep}:e.type==="run.record.completed"?{type:e.type,itemId:e.itemId}:{type:e.type,itemId:e.itemId,stepId:e.stepId}}s(Ro,"summarizeRunExecutionEvent");function qt(e,t,r){e.subscribers.add(t);for(let o of e.events)r(t,o.type,o);t.on("close",()=>{e.subscribers.delete(t)})}s(qt,"attachRunSubscriber");function Kt(e,t,r){e.events.push(t),e.events.length>100&&e.events.shift();for(let o of e.subscribers){if(o.destroyed||o.writableEnded){e.subscribers.delete(o);continue}try{r(o,t.type,t)}catch{e.subscribers.delete(o)}}}s(Kt,"publishRunEvent");function Ao(e){for(let t of e.subscribers)t.end();e.subscribers.clear()}s(Ao,"closeRunSubscribers");function bo(e){return{code:Gt(e),message:Vt(e),details:Yt(e)}}s(bo,"apiError");function Gt(e){return typeof e=="object"&&e!==null&&"code"in e&&typeof e.code=="string"?e.code:"INTERNAL_ERROR"}s(Gt,"errorCode");function Vt(e){return e instanceof Error?e.message:String(e)}s(Vt,"errorMessage");function Yt(e){if(typeof e=="object"&&e!==null&&"details"in e)return e.details}s(Yt,"errorDetails");import Io from"path";import{loadWorkflowTemplate as xo}from"aitable-workflow-core";function Qt(e){let t=s(r=>r.env.services.projectStore.workflows?null:F(r,501,"WORKFLOWS_NOT_CONFIGURED","\u5DE5\u4F5C\u6D41\u6301\u4E45\u5316\u672A\u914D\u7F6E\uFF08\u9700\u8981 AITABLE_DB_URL\uFF09"),"notConfigured");e.get("/:projectId/workflows",async r=>{let{services:o,scope:n}=r.env;return o.projectStore.workflows?w(r,{workflows:await o.projectStore.workflows.list(r.env.project.id,n)}):w(r,{workflows:await Eo(r.env.project)})}),e.post("/:projectId/workflows",async r=>{let o=t(r);if(o)return o;let{services:n,scope:i,incoming:a}=r.env,c=r.env.project,u=await I(a);if(!u.name||!u.name.trim())return F(r,422,"INVALID_INPUT","\u5DE5\u4F5C\u6D41\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");let p=await n.projectStore.workflows.create(c.id,{name:u.name,templatePath:u.templatePath,files:u.files},i);await O(c,n);let l={workflow:p.workflow};return p.files&&(l.files=p.files),p.warnings&&p.warnings.length>0&&(l.warnings=p.warnings),w(r,l)}),e.get("/:projectId/workflows/:workflowId",async r=>{let{services:o,scope:n}=r.env;if(!o.projectStore.workflows){let a=await _o(r.env.project,r.req.param("workflowId")??"");return a?w(r,a):F(r,404,"WORKFLOW_NOT_FOUND","\u5DE5\u4F5C\u6D41\u4E0D\u5B58\u5728")}let i=await o.projectStore.workflows.exportWorkflow(r.env.project.id,r.req.param("workflowId")??"",n);return w(r,{workflow:i.workflow,files:i.files})}),e.delete("/:projectId/workflows/:workflowId",async r=>{let o=t(r);if(o)return o;let{services:n,scope:i}=r.env,a=r.env.project;if(n.runtime.runSessions.has(a.id))return F(r,409,"project_busy","\u9879\u76EE\u5B58\u5728\u6D3B\u8DC3\u7684\u8FD0\u884C\u4F1A\u8BDD\uFF0C\u65E0\u6CD5\u5220\u9664\u5DE5\u4F5C\u6D41");let c=await n.projectStore.workflows.resolve(a.id,r.req.param("workflowId")??"",i);return await n.projectStore.workflows.delete(c,i),await O(a,n),w(r,{ok:!0,id:c.id})}),e.put("/:projectId/workflows/:workflowId/files",async r=>{let o=t(r);if(o)return o;let{services:n,scope:i,incoming:a}=r.env,c=r.env.project,u=await n.projectStore.workflows.resolve(c.id,r.req.param("workflowId")??"",i),p=await I(a);if(!Array.isArray(p.files))return F(r,422,"INVALID_INPUT","files \u4E0D\u80FD\u4E3A\u7A7A");let l=await n.projectStore.workflows.replaceFiles(u,p.files,i);return await O(c,n),w(r,{workflow:u,files:l})}),e.patch("/:projectId/workflows/:workflowId/files",async r=>{let o=t(r);if(o)return o;let{services:n,scope:i,incoming:a}=r.env,c=r.env.project,u=await n.projectStore.workflows.resolve(c.id,r.req.param("workflowId")??"",i),p=await I(a),l=await n.projectStore.workflows.updateFiles(u,{upsert:p.upsert,remove:p.remove},i);return await O(c,n),w(r,{workflow:u,files:l})})}s(Qt,"registerWorkflowRoutes");async function Eo(e){let t=await D(e),r=Zt(t);return(await Promise.all(r.map(n=>rr(e,n,t)))).sort((n,i)=>n.name.localeCompare(i.name))}s(Eo,"listLocalWorkflows");async function _o(e,t){let r=await D(e),o=Zt(r).find(c=>Xt(c)===t);if(!o)return null;let n=await rr(e,o,r),i=er(o),a=r.filter(c=>tr(c.path,o)).map(c=>({...c,path:c.path.slice(i.length)}));return{workflow:n,files:a}}s(_o,"exportLocalWorkflow");function Zt(e){return e.map(t=>t.path).filter(t=>t==="workflow.yml"||/^workflows\/[^/]+\/workflow\.ya?ml$/.test(t))}s(Zt,"localWorkflowTemplatePaths");function Xt(e){return e==="workflow.yml"?"main":e.split("/")[1]??"main"}s(Xt,"localWorkflowId");function er(e){return e==="workflow.yml"?"":e.replace(/workflow\.ya?ml$/,"")}s(er,"workflowBase");function tr(e,t){let r=er(t);return r?e.startsWith(r):e==="workflow.yml"||e==="views.yml"||e.startsWith("recipes/")}s(tr,"belongsToWorkflow");async function rr(e,t,r){let o=Xt(t),n=await xo(Io.join(e.dir,t)).catch(a=>(console.warn(`\u52A0\u8F7D\u672C\u5730\u5DE5\u4F5C\u6D41\u6A21\u677F\u5931\u8D25 [${t}]\uFF1A`,a instanceof Error?a.message:String(a)),null)),i=r.filter(a=>tr(a.path,t)).length;return{id:o,projectId:e.id,name:n?.meta.name??o,domain:n?.meta.domain,templatePath:t,tableId:n?.tracker?.table_id,fileCount:i,createdAt:e.createdAt,updatedAt:e.updatedAt}}s(rr,"buildLocalWorkflow");function Fo(){let e=process.env.AITABLE_WORKFLOW_SSE_HEARTBEAT_MS,t=typeof e=="string"?e.trim():"";if(!t)return 15e3;let r=Number.parseInt(t,10);return Number.isFinite(r)&&r>0?r:15e3}s(Fo,"readSseHeartbeatMs");function z(e){e.writeHead(200,{"content-type":"text/event-stream; charset=utf-8","cache-control":"no-cache, no-transform",connection:"keep-alive"}),Wo(e)}s(z,"startSse");function Wo(e){let t=Fo(),r=setInterval(()=>{if(e.writableEnded||!e.writable){clearInterval(r);return}try{e.write(`:keepalive
7
+ `)}o(Ze,"writeDesignSse");function Tt(e,t){let r=e.buffer.push(t);for(let s of e.subscribers){if(s.destroyed||s.writableEnded){e.subscribers.delete(s);continue}try{Ze(s,r.seq,t)}catch{e.subscribers.delete(s)}}}o(Tt,"publishDesignEvent");function St(e){ge.delete(e);let t=Te.get(e);if(Te.delete(e),!!t){for(let r of t.subscribers)if(!r.writableEnded)try{r.end()}catch{}t.subscribers.clear()}}o(St,"closeDesignSession");function Dt(e,t){let r=e.searchParams.get("cursor")??wt(t.headers["last-event-id"]);if(r==null||r==="")return;let s=Number(r);return Number.isFinite(s)?s:void 0}o(Dt,"parseReconnectCursor");function Ts(e,t){return e.runtime.runSessions.has(t)||ge.has(t)}o(Ts,"isStageActiveFor");function b(e,t){return t.projectStore.prepareWorkdir(e,{isStageActive:o(()=>Ts(t,e.id),"isStageActive")})}o(b,"prepareWorkdirGuarded");async function Se(e,t,r){try{let n=(await e.projectStore.instances.getState(t)).stages[r]?.task;if(!n)return;let i=n.status;if(i!=="succeeded"&&i!=="failed"&&i!=="cancelled")return;await e.projectStore.commitWorkdir(t,{stage:r})}catch(s){ks.warn("stage-sync","stage \u6D41\u56DE\u5199\u6743\u5A01\u6E90\u5931\u8D25",{projectId:t.id,stage:r,error:x(s)})}}o(Se,"commitStageOnTerminal");async function jt(e,t){await e.projectStore.cascadeDelete(t).catch(()=>{}),await e.execution.unbindProjectMachine(t).catch(()=>{})}o(jt,"cascadeDeleteProject");import Ee from"path";import{readFile as Bt,writeFile as As,access as Es,readdir as _s}from"fs/promises";import ot from"yaml";import{scaffold as Fs,writeProfileRoles as Ws,isRecord as _e}from"aitable-workflow-core";import{createHash as Ss,randomUUID as Ds}from"crypto";import{mkdir as Xe,readdir as xt,readFile as et,rm as Ot,stat as Rt,writeFile as bt}from"fs/promises";import O from"path";var js=O.resolve(process.cwd(),".sandbox","aitable-workflow-server"),It=/^[a-zA-Z0-9_-]+$/,xs=/\.(json|ya?ml|ts|js|md|txt)$/i,Os=["workflow.yml","views.yml","recipes","traces"];function At(e={}){let t=O.resolve(e.dataRoot??process.env.AITABLE_WORKFLOW_DATA_DIR??js);return{dataRoot:t,projectsRoot:O.join(t,"projects")}}o(At,"createWorkspace");async function Et(e){await Xe(e.projectsRoot,{recursive:!0});let t=new Date().toISOString(),r=(await tt(e)).length+1,s={id:`wf_${Ds().slice(0,8)}`,title:`\u5DE5\u4F5C\u6D41 #${r}`,dir:"",createdAt:t,updatedAt:t};return s.dir=Lt(e,s.id),await Xe(s.dir,{recursive:!0}),await Ae(s),s}o(Et,"createProject");async function we(e,t){let r=await E({dataRoot:O.dirname(O.dirname(e.dir)),projectsRoot:O.dirname(e.dir)},e.id),s={...r,...t,title:t.title?.trim()||r.title,updatedAt:new Date().toISOString()};return await Ae(s),s}o(we,"updateProjectMeta");async function tt(e){let t=await xt(e.projectsRoot,{withFileTypes:!0}).catch(()=>[]);return(await Promise.all(t.filter(s=>s.isDirectory()&&It.test(s.name)).map(s=>E(e,s.name).catch(()=>null)))).filter(s=>s!==null).sort((s,n)=>n.updatedAt.localeCompare(s.updatedAt))}o(tt,"listProjects");async function E(e,t){Mt(t);let r=Lt(e,t);if(!(await Rt(r).catch(()=>null))?.isDirectory())throw Object.assign(new Error(`\u9879\u76EE\u4E0D\u5B58\u5728: ${t}`),{code:"PROJECT_NOT_FOUND",status:404});let n=O.join(r,"project.json"),i=await et(n,"utf8").catch(()=>"");if(!i){let c=new Date().toISOString(),u={id:t,dir:r,createdAt:c,updatedAt:c};return await Ae(u),u}return{...JSON.parse(i),dir:r}}o(E,"resolveProject");async function L(e){let t={...e,updatedAt:new Date().toISOString()};return await Ae(t),t}o(L,"touchProject");async function _t(e){return await Promise.all(Os.map(t=>Ot(O.join(e.dir,t),{recursive:!0,force:!0}))),await L(e)}o(_t,"resetProjectWorkflow");async function Ie(e){await Ot(e.dir,{recursive:!0,force:!0})}o(Ie,"deleteProject");async function Rs(e){let t=await et(Nt(e),"utf8").catch(()=>"");return t.trim()?JSON.parse(t):null}o(Rs,"readDesignManifest");async function rt(e,t){await Xe(Wt(e),{recursive:!0}),await bt(Nt(e),`${JSON.stringify(t,null,2)}
8
+ `,"utf8")}o(rt,"writeDesignManifest");async function Ft(e){let t=await Rs(e);if(!t)return{status:"missing_manifest",manifest:null,errors:["\u7F3A\u5C11 Design \u4EA7\u7269\u6E05\u5355"],missing:["runtime/artifacts/design-manifest.json"]};if(t.status!=="ready"||!t.validation.passed)return{status:"invalid",manifest:t,errors:t.validation.errors.length?t.validation.errors:["Design \u4EA7\u7269\u6E05\u5355\u672A\u5C31\u7EEA"],missing:[]};let r=t.generated.filter(n=>n.required).map(n=>n.path),s=[];for(let n of r)(await Rt(O.join(e.dir,n)).catch(()=>null))?.isFile()||s.push(n);return s.length>0?{status:"artifact_missing",manifest:t,errors:[`\u7F3A\u5C11 Design \u4EA7\u7269\uFF1A${s.join(", ")}`],missing:s}:{status:"ready",manifest:t,errors:[],missing:[]}}o(Ft,"validateDesignArtifacts");function De(e){return Ss("sha256").update(e).digest("hex")}o(De,"checksumContent");async function R(e){let t=[];return await Ct(e.dir,e.dir,t),t.sort((r,s)=>r.path.localeCompare(s.path))}o(R,"listProjectFiles");function J(e){return O.join(e.dir,"aitable-workflow.config.yml")}o(J,"configPath");function st(e,t,r){let s=new URLSearchParams({entrance:"data",sheetId:t});return r?.trim()&&s.set("viewId",r.trim()),`https://alidocs.dingtalk.com/i/nodes/${e}?iframeQuery=${encodeURIComponent(s.toString())}`}o(st,"buildAITableUrl");function bs(e){return O.join(e.dir,"runtime")}o(bs,"runtimeDir");function Wt(e){return O.join(bs(e),"artifacts")}o(Wt,"artifactsDir");function Nt(e){return O.join(Wt(e),"design-manifest.json")}o(Nt,"designManifestPath");function Lt(e,t){return Mt(t),O.join(e.projectsRoot,t)}o(Lt,"projectDir");function Mt(e){if(!It.test(e))throw Object.assign(new Error(`\u975E\u6CD5\u9879\u76EE ID: ${e}`),{code:"INVALID_PROJECT_ID"})}o(Mt,"assertProjectId");async function Ae(e){await bt(O.join(e.dir,"project.json"),`${JSON.stringify(e,null,2)}
9
+ `,"utf8")}o(Ae,"writeProjectMeta");async function Ct(e,t,r){let s=await xt(t,{withFileTypes:!0}).catch(()=>[]);for(let n of s){if(Is(e,O.join(t,n.name),n.name))continue;let i=O.join(t,n.name);if(n.isDirectory()){await Ct(e,i,r);continue}if(!n.isFile()||!xs.test(n.name))continue;let a=O.relative(e,i).split(O.sep).join("/"),c=await et(i,"utf8").catch(()=>"");r.push({path:a,content:c})}}o(Ct,"walk");function Is(e,t,r){return r==="node_modules"||r==="traces"||r==="runtime"||r===".aitable"||r==="project.json"?!0:O.relative(e,t).split(O.sep).join("/").startsWith(".aitable/")}o(Is,"isHiddenProjectFile");function $t(e,t=1){let r=e.replace(/\s+/g," ").trim();if(!r)return`\u5DE5\u4F5C\u6D41 #${t}`;let s=r.split(/[。.!!?\n]/u)[0]?.split(/[::,,;;]/u)[0]?.trim();return s?s.length>16?s.slice(0,16):s:`\u5DE5\u4F5C\u6D41 #${t}`}o($t,"deriveProjectTitleFromSop");async function qt(e,t,r,s,n,i){let a=await E(t,e),c=await W(s),u=await r.createTask(a,"init");try{let p=Ls(c),f=await Fs({answers:p,cwd:a.dir,force:!0}),g=await Ws(Ee.join(a.dir,"profile","profile.yml"),c.roles??[]);c.machineId&&i&&await i(e,c.machineId).catch(()=>{});let v=await L(a),P=g.skippedKnowledgeBindings.map(y=>`\u89D2\u8272 ${y.roleId} \u7684\u77E5\u8BC6\u5E93\u7ED1\u5B9A\u672A\u5199\u5165 profile\uFF0C\u540E\u7EED\u5E94\u4E0B\u6C89\u5230\u90E8\u7F72 Binding\uFF1A${y.knowledge.join(", ")}`);await r.updateTask(a,u.id,{status:"succeeded",checkpoint:"done",currentAction:"\u914D\u7F6E\u5DF2\u5B8C\u6210",outputs:{warnings:P,roleFiles:[]}});let d=$t(c.sop,1),k=await we(v,{title:d});m(n,{project:k,scaffold:f,roleFiles:[],files:await R(v),warnings:P})}catch(p){throw await r.updateTask(a,u.id,{status:"failed",checkpoint:"init_failed",currentAction:"\u914D\u7F6E\u751F\u6210\u5931\u8D25",error:{code:B(p),message:x(p),retryable:!0,details:U(p)}}),p}}o(qt,"handleInit");async function Ut(e,t,r,s,n){let i=await E(t,e),c=(await W(s)).tableId?.trim()??"";if(!c)throw Object.assign(new Error("Table Id \u4E0D\u80FD\u4E3A\u7A7A"),{code:"TABLE_ID_REQUIRED",status:422});let u=J(i),p=await Bt(u,"utf8"),f=ot.parse(p);if(!_e(f)||!_e(f.tracker))throw Object.assign(new Error("\u914D\u7F6E\u6587\u4EF6\u7F3A\u5C11 tracker\uFF0C\u65E0\u6CD5\u5199\u56DE Table Id"),{code:"CONFIG_TRACKER_MISSING",status:500});f.tracker.table_id=c,await As(u,ot.stringify(f),"utf8");let g=await L(i);m(n,{project:g,files:await R(g),state:await r.getRecoverableState(g)})}o(Ut,"handleUpdateProjectTableId");async function Jt(e,t,r,s,n,i,a){let c=await E(t,e),u=await W(i);if(u.action!=="confirm"&&u.action!=="terminate")throw Object.assign(new Error("\u4EBA\u5DE5\u64CD\u4F5C\u53EA\u652F\u6301 confirm \u6216 terminate"),{code:"INVALID_HUMAN_ACTION",status:422});let p=s.runSessions.get(e)?.app;if(!p)throw Object.assign(new Error("\u8FD0\u884C\u670D\u52A1\u672A\u542F\u52A8\uFF0C\u65E0\u6CD5\u5904\u7406\u4EBA\u5DE5\u786E\u8BA4\u64CD\u4F5C"),{code:"RUN_SESSION_REQUIRED",status:409});let f=u.action==="confirm"?p.settings.tracker.states.active[0]:p.settings.tracker.states.terminal.find(d=>d==="\u5DF2\u5173\u95ED")??p.settings.tracker.states.terminal.at(-1);if(!f)throw Object.assign(new Error("\u672A\u914D\u7F6E\u53EF\u7528\u4E8E\u4EBA\u5DE5\u64CD\u4F5C\u7684\u76EE\u6807\u72B6\u6001"),{code:"HUMAN_ACTION_STATE_MISSING",status:500});let g=u.action==="confirm"?"\u4EBA\u5DE5\u786E\u8BA4\uFF1A\u7EE7\u7EED\u6D41\u7A0B":"\u4EBA\u5DE5\u786E\u8BA4\uFF1A\u7EC8\u6B62\u6D41\u7A0B",v=null;u.action==="confirm"&&(v=await Ns(p,n)),await p.tracker.updateState(n,f),v&&await p.tracker.updateCurrentStep(n,v),await p.tracker.appendEvent(n,g);let P=await r.updateRunSnapshotRecord(c,n,u.action==="confirm"?{state:f,status:"running",currentStepId:v??void 0}:{state:f,status:"done",currentStepId:null},g);m(a,{action:u.action,recordId:n,state:P})}o(Jt,"handleRunHumanAction");async function Ns(e,t){let[r]=await e.tracker.fetchItemsByIds([t]).catch(()=>[]),s=r?.currentStep;if(!s)return null;let n=e.workflow.orderedStepIds.indexOf(s);return n<0?null:e.workflow.orderedStepIds[n+1]??null}o(Ns,"resolveNextStepId");function Ls(e){let t=e.tableBaseId.trim();if(!t)throw Object.assign(new Error("Base Id \u4E0D\u80FD\u4E3A\u7A7A"),{code:"BASE_ID_REQUIRED",status:422});let r=e.llmProvider.trim();return{agentType:Cs(e.agentsSelected),scene:Ms(e),baseId:t,tableId:e.tableId?.trim()??"",llmProvider:r,llmModel:r?e.llmModel.trim()||$s(r):"",llmApiKeyEnv:r?Bs(r):"",confirmed:!0}}o(Ls,"toInitAnswers");function Ms(e){return e.sop.trim()||"\u7528\u6237\u5728 AI \u8868\u683C\u4E2D\u63D0\u4EA4\u4E1A\u52A1\u8BB0\u5F55\uFF0C\u7CFB\u7EDF\u81EA\u52A8\u5B8C\u6210\u5206\u7C7B\u3001\u5904\u7406\u3001\u5BA1\u6838\u4E0E\u56DE\u5199\u3002"}o(Ms,"buildScene");function Cs(e){return e.includes("qwenwork")?"qwenwork":e.includes("qoder")?"qoder":e.includes("codex")?"codex":"claudecode"}o(Cs,"toAgentType");function $s(e){return e==="openai"?"gpt-4o":e==="anthropic"?"claude-sonnet-4-20250514":e==="dashscope"?"qwen-max":"Qwen3.7-Max-DogFooding"}o($s,"defaultModel");function Bs(e){return e==="openai"?"OPENAI_API_KEY":e==="anthropic"?"ANTHROPIC_API_KEY":e==="dashscope"?"DASHSCOPE_API_KEY":e==="idealab"?"IDEALAB_API_KEY":"LLM_API_KEY"}o(Bs,"defaultApiKeyEnv");async function Ht(e,t){let r=Ee.join(e.dir,"aitable-workflow.config.yml");try{await Es(r)}catch{t.warnings.push("aitable-workflow.config.yml \u4E0D\u5B58\u5728\uFF08\u9879\u76EE\u7EA7\u914D\u7F6E\u7F3A\u5931\uFF0C\u8FD0\u884C\u524D\u9700\u8981\u751F\u6210\uFF09")}await qs(e.dir)||t.warnings.push("\u672A\u627E\u5230\u4EFB\u4F55\u89D2\u8272\u5B9A\u4E49\uFF08profile/profile.yml \u7684 roles \u6BB5\u6216 roles/*.role.yml\uFF0Cdesign \u9636\u6BB5 requiresAgent \u7684 step \u9700\u7ED1\u5B9A role\uFF09")}o(Ht,"augmentProjectFileValidation");async function qs(e){try{let r=ot.parse(await Bt(Ee.join(e,"profile","profile.yml"),"utf8"));if(_e(r)&&_e(r.roles)&&Object.keys(r.roles).length>0)return!0}catch{}return(await _s(Ee.join(e,"roles")).catch(()=>[])).some(r=>r.endsWith(".role.yml")||r.endsWith(".role.yaml"))}o(qs,"hasAnyRoleDefinition");import zt from"fs";import Us from"path";var Js=/requiresAgent\s*:\s*(true|false)/,Gt=new Map;function Kt(e,t){if(!t)return!1;for(let r of[".recipe.ts",".recipe.js"]){let s=Us.join(e,`${t}${r}`);try{let n=zt.statSync(s);if(!n.isFile())continue;let i=`${n.mtimeMs}:${n.size}`,a=Gt.get(s);if(a?.signature===i)return a.requiresAgent;let u=zt.readFileSync(s,"utf8").match(Js),p=u?u[1]==="true":!1;return Gt.set(s,{signature:i,requiresAgent:p}),p}catch(n){if(n.code==="ENOENT")continue;return!1}}return!1}o(Kt,"readRecipeRequiresAgent");import{rm as Yt,mkdir as Hs,writeFile as zs}from"fs/promises";import je from"path";import Gs from"yaml";import{isRecord as Ks,resolveInsideRoot as Vs}from"aitable-workflow-core";var Vt=/\.(json|ya?ml|ts|js|md|txt)$/i,Ys=new Set(["runtime","traces","node_modules"]),Qt="aitable-workflow.config.yml";function Zt(e,t){try{Vs(e,t,{allowRoot:!1})}catch{throw Object.assign(new Error(`\u8DEF\u5F84\u8D8A\u51FA\u9879\u76EE\u76EE\u5F55: ${t}`),{code:"invalid_path",status:400})}}o(Zt,"assertSafeRelativePath");function Xt(e,t){Zt(e.dir,t)}o(Xt,"assertSafeProjectPath");function ma(e){Zt("/workflow-root",e)}o(ma,"assertSafeWorkflowPath");function er(e){let r=rr(e).split("/")[0]??"";return Ys.has(r)}o(er,"isForbiddenWritable");function Qs(e){if(er(e))throw Object.assign(new Error(`\u7981\u6B62\u5199\u5165\u53D7\u4FDD\u62A4\u76EE\u5F55: ${e}`),{code:"invalid_path",status:400});if(!Vt.test(e))throw Object.assign(new Error(`\u6587\u4EF6\u7C7B\u578B\u4E0D\u5141\u8BB8\u5199\u5165: ${e}\uFF08\u4EC5\u5141\u8BB8 ${Vt.source}\uFF09`),{code:"invalid_path",status:400})}o(Qs,"assertWritableKind");function tr(e){let t=rr(e);return t===Qt||t.endsWith(".config.yml")?"config":t==="workflow.yml"?"workflow":t==="views.yml"?"views":t.startsWith("recipes/")?"recipe":t.startsWith("roles/")?"role":"other"}o(tr,"kindOf");function rr(e){return je.posix.normalize(e.replace(/\\/g,"/")).replace(/^\.\//,"")}o(rr,"normalizeLogicalPath");function Zs(e){let t=[],r;try{r=Gs.parse(e)}catch(n){return t.push(`aitable-workflow.config.yml \u89E3\u6790\u5931\u8D25: ${n instanceof Error?n.message:String(n)}`),t}if(!Ks(r))return t.push("aitable-workflow.config.yml \u9876\u5C42\u5E94\u4E3A\u5BF9\u8C61"),t;let s=r.scene;return(typeof s!="string"||s.trim()==="")&&t.push("aitable-workflow.config.yml \u7F3A\u5C11 scene \u5B57\u6BB5\uFF08\u5EFA\u8BAE\u586B\u5199\u4E1A\u52A1\u573A\u666F\u63CF\u8FF0\uFF09"),t}o(Zs,"validateConfigScene");async function Fe(e,t){let r=[],s=[];for(let n of t){Xt(e,n.path),Qs(n.path);let i=je.join(e.dir,n.path);await Hs(je.dirname(i),{recursive:!0}),await zs(i,n.content,"utf8"),r.push({path:n.path,content:n.content}),n.path===Qt&&s.push(...Zs(n.content))}return{written:r,warnings:s}}o(Fe,"writeProjectFiles");async function sr(e,t){for(let r of t){if(Xt(e,r),er(r))throw Object.assign(new Error(`\u7981\u6B62\u5220\u9664\u53D7\u4FDD\u62A4\u76EE\u5F55: ${r}`),{code:"invalid_path",status:400});await Yt(je.join(e.dir,r),{force:!0})}}o(sr,"removeProjectFiles");async function or(e,t){let r=await Xs(e);for(let s of r)await Yt(je.join(e.dir,s),{force:!0}).catch(()=>{});return Fe(e,t)}o(or,"replaceProjectFiles");async function Xs(e){return(await R(e)).map(r=>r.path).filter(r=>tr(r)!=="other")}o(Xs,"collectBusinessFiles");function eo(e){return De(e.content)}o(eo,"checksumFile");async function nr(e){let t=await R(e),r=t.map(s=>({path:s.path,sha256:eo(s)}));return{project:e,files:t,checksums:r,exportedAt:new Date().toISOString()}}o(nr,"buildProjectExport");var to="coding-agents.config.json";function ir(e){let t=[],r=e.files,s=e.checksums,n=r.findIndex(i=>i.path===to);if(n>=0){let i=r[n],a=i.content;try{let c=JSON.parse(i.content);if(c&&typeof c=="object"&&!Array.isArray(c)){let u=c.agents;if(u&&typeof u=="object"&&!Array.isArray(u)){let p=!1;for(let f of Object.keys(u)){let g=u[f];if(!g||typeof g!="object"||Array.isArray(g))continue;let v=g.env;if(v&&typeof v=="object"&&!Array.isArray(v)){let P=v;if(Object.values(P).some(k=>typeof k=="string"&&k!=="")){for(let k of Object.keys(P))P[k]="";p=!0}}}p&&(a=JSON.stringify(c,null,2)+`
10
+ `)}}}catch{}if(a!==i.content){let c={path:i.path,content:a};r=r.map((p,f)=>f===n?c:p);let u=De(a);s=s.map((p,f)=>f===n?{path:p.path,sha256:u}:p),t.push(i.path)}}return{...e,files:r,checksums:s,redacted:t.length>0?t:e.redacted}}o(ir,"redactExportBundle");import{logger as ar}from"aitable-workflow-core";function We(e,t){let r={...e.stages,[t.stage]:xe(t)};return Y({id:t.projectId,dir:"",createdAt:"",updatedAt:t.updatedAt},{...e,projectId:t.projectId,activeStage:nt(t.status)?t.stage:e.activeStage===t.stage?null:e.activeStage,stages:r,updatedAt:t.updatedAt})}o(We,"mergeTaskIntoState");function Y(e,t){let r=new Date().toISOString(),s=t.stages??{};return{projectId:t.projectId??e.id,activeStage:t.activeStage??ro(s),stages:s,runSnapshot:t.runSnapshot,updatedAt:t.updatedAt??e.updatedAt??r}}o(Y,"normalizeProjectState");function xe(e){let t=so(e);return t?{task:e,recoveryAction:t}:{task:e}}o(xe,"stageStateOf");function q(e){if(!e.activeStage)return null;let t=e.stages[e.activeStage]?.task;return t&&nt(t.status)?t:null}o(q,"activeTaskOf");function nt(e){return e==="running"||e==="blocked"}o(nt,"isActiveStatus");function Ne(e){return e==="failed"||e==="succeeded"||e==="cancelled"}o(Ne,"isTerminalStatus");function ro(e){for(let[t,r]of Object.entries(e))if(r&&nt(r.task.status))return t;return null}o(ro,"findActiveStage");function so(e){if(e.status==="blocked"&&e.stage==="design"&&e.block?.reason==="discovery_required")return{type:"confirm_input",label:e.block.actionLabel,description:e.block.message,api:{method:"POST",path:`/api/projects/${e.projectId}/design/discovery/confirm`}};if(e.status==="blocked")return{type:"continue",label:e.block?.actionLabel??oo(e.stage),description:e.block?.message??e.currentAction,api:{method:"POST",path:`/api/projects/${e.projectId}/tasks/${e.stage}/continue`}};if(e.status==="failed"&&e.error?.retryable)return{type:"retry",label:no(e.stage),description:e.error.message,api:{method:"POST",path:io(e)}}}o(so,"recoveryActionForTask");function oo(e){return e==="setup"?"\u7EE7\u7EED\u642D\u5EFA":e==="design"?"\u7EE7\u7EED\u8BBE\u8BA1":e==="run"?"\u7EE7\u7EED\u8FD0\u884C":"\u7EE7\u7EED"}o(oo,"continueLabel");function no(e){return e==="design"?"\u91CD\u65B0\u751F\u6210\u5DE5\u4F5C\u6D41\u8BBE\u8BA1":e==="setup"?"\u91CD\u8BD5\u642D\u5EFA":e==="run"?"\u91CD\u8BD5\u8FD0\u884C":"\u91CD\u8BD5"}o(no,"retryLabel");function io(e){return e.stage==="design"?`/api/projects/${e.projectId}/design`:`/api/projects/${e.projectId}/tasks/${e.stage}/retry`}o(io,"retryPath");function ur(e){return{appFactory:e,runSessions:new Map}}o(ur,"createServerRuntime");async function pr(e,t,r,s,n,i){let a=await E(t,e),c=await r.getState(a),u=s.runSessions.get(e);if(u){cr(u,n,i);let v=await r.getState(a),P=q(v),d=P?.stage==="run"?P:v.stages.run?.task;d&&lr(u,{type:"task.snapshot",task:d},i);return}let p=q(c);if(p?.stage==="run"&&p.status==="running"){let P={type:"task.failed",task:await r.updateTask(a,p.id,{status:"failed",checkpoint:"session_lost",currentAction:"\u8FD0\u884C\u670D\u52A1\u8FDE\u63A5\u5DF2\u65AD\u5F00",error:{code:"RUN_SESSION_LOST",message:"\u540E\u7AEF\u8FD0\u884C\u4F1A\u8BDD\u5DF2\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u662F\u670D\u52A1\u91CD\u542F\u3002\u8BF7\u91CD\u8BD5\u8FD0\u884C\u3002",retryable:!0}})};i(n,P.type,P),i(n,"error",{type:"error",error:{code:"RUN_SESSION_LOST",message:"\u540E\u7AEF\u8FD0\u884C\u4F1A\u8BDD\u5DF2\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u662F\u670D\u52A1\u91CD\u542F\u3002\u8BF7\u91CD\u8BD5\u8FD0\u884C\u3002"}}),n.end();return}let f=await r.createTask(a,"run"),g={taskId:f.id,subscribers:new Set,events:[],app:null,snapshotWrite:Promise.resolve()};s.runSessions.set(e,g),cr(g,n,i),ao(a,f,g,s,r,i)}o(pr,"handleRunRuntime");function dr(e,t){if(!t)return e;let r=e.stages.run?.task;return!r||r.status==="running"?e:{...e,activeStage:"run",stages:{...e.stages,run:{task:{...r,id:t.taskId,status:"running",checkpoint:"listening",currentAction:"\u771F\u5B9E\u8FD0\u884C\u670D\u52A1\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u76D1\u542C AI \u8868\u683C\u4E2D\u7684\u5F85\u5904\u7406\u8BB0\u5F55",block:void 0,error:void 0}}}}}o(dr,"overlayRunSessionState");async function ao(e,t,r,s,n,i){let a=o(u=>{lr(r,u,i),uo(u)&&(ar.info("run-runtime","publish run execution event",po(u)),r.snapshotWrite=r.snapshotWrite.then(()=>n.appendRunSnapshotEvent(e,u)).catch(p=>{ar.error("run-runtime","\u5199\u5165\u8FD0\u884C\u5FEB\u7167\u5931\u8D25",{projectId:e.id,event:u.type,error:p instanceof Error?p.message:String(p)})}))},"send"),c=o(async(u,p)=>{let f=await n.updateTask(e,t.id,{currentAction:u,checkpoint:p});a({type:"task.progress",taskId:f.id,message:u,checkpoint:p}),a({type:"run.progress",message:u})},"progress");try{a({type:"task.started",task:t}),await c("\u52A0\u8F7D\u771F\u5B9E\u8FD0\u884C\u914D\u7F6E","loading");let u=await s.appFactory(J(e),f=>a(co(f)));r.app=u,await c("\u771F\u5B9E\u8FD0\u884C\u670D\u52A1\u5DF2\u8FDE\u63A5","started"),a({type:"run.started",projectId:e.id,workflowName:u.workflow.meta.name,stepCount:u.workflow.orderedStepIds.length,intervalMs:u.settings.polling.interval_ms}),await c("\u771F\u5B9E\u8FD0\u884C\u670D\u52A1\u5DF2\u542F\u52A8\uFF0C\u5F00\u59CB\u76D1\u542C AI \u8868\u683C\u4E2D\u7684\u5F85\u5904\u7406\u8BB0\u5F55","listening"),await u.start();let p=await n.updateTask(e,t.id,{status:"succeeded",checkpoint:"stopped",currentAction:"\u771F\u5B9E\u8FD0\u884C\u670D\u52A1\u5DF2\u505C\u6B62"});a({type:"task.succeeded",task:p}),a({type:"run.stopped",message:"\u771F\u5B9E\u8FD0\u884C\u670D\u52A1\u5DF2\u505C\u6B62"})}catch(u){let p=await n.updateTask(e,t.id,{status:"failed",checkpoint:"failed",currentAction:"\u771F\u5B9E\u8FD0\u884C\u542F\u52A8\u5931\u8D25",error:{code:fr(u),message:mr(u),retryable:!0,details:gr(u)}});a({type:"task.failed",task:p}),a({type:"error",error:fo(u)})}finally{s.runSessions.delete(e.id),lo(r)}}o(ao,"startRunSession");function co(e){switch(e.type){case"record.observed":return{type:"run.record.detected",record:e.record};case"step.started":return{type:"run.step.started",itemId:e.itemId,stepId:e.stepId,stepName:e.stepName};case"step.succeeded":return{type:"run.step.succeeded",itemId:e.itemId,stepId:e.stepId,stepName:e.stepName,logs:e.logs};case"step.failed":return{type:"run.step.failed",itemId:e.itemId,stepId:e.stepId,stepName:e.stepName,error:e.error,logs:e.logs};case"record.blocked":return{type:"run.record.blocked",itemId:e.itemId,stepId:e.stepId,stepName:e.stepName,reason:e.reason,logs:e.logs};case"record.completed":return{type:"run.record.completed",itemId:e.itemId}}}o(co,"toRunEventDTO");function uo(e){return e.type.startsWith("run.record.")||e.type.startsWith("run.step.")}o(uo,"isRunExecutionEvent");function po(e){return e.type==="run.record.detected"?{type:e.type,itemId:e.record.id,state:e.record.state,currentStep:e.record.currentStep}:e.type==="run.record.completed"?{type:e.type,itemId:e.itemId}:{type:e.type,itemId:e.itemId,stepId:e.stepId}}o(po,"summarizeRunExecutionEvent");function cr(e,t,r){e.subscribers.add(t);for(let s of e.events)r(t,s.type,s);t.on("close",()=>{e.subscribers.delete(t)})}o(cr,"attachRunSubscriber");function lr(e,t,r){e.events.push(t),e.events.length>100&&e.events.shift();for(let s of e.subscribers){if(s.destroyed||s.writableEnded){e.subscribers.delete(s);continue}try{r(s,t.type,t)}catch{e.subscribers.delete(s)}}}o(lr,"publishRunEvent");function lo(e){for(let t of e.subscribers)t.end();e.subscribers.clear()}o(lo,"closeRunSubscribers");function fo(e){return{code:fr(e),message:mr(e),details:gr(e)}}o(fo,"apiError");function fr(e){return typeof e=="object"&&e!==null&&"code"in e&&typeof e.code=="string"?e.code:"INTERNAL_ERROR"}o(fr,"errorCode");function mr(e){return e instanceof Error?e.message:String(e)}o(mr,"errorMessage");function gr(e){if(typeof e=="object"&&e!==null&&"details"in e)return e.details}o(gr,"errorDetails");var T=class{ctx;serverServices;async list(){let t=await this.serverServices.projectStore.projects.list(A(this.ctx));this.ctx.respond=!1,m(this.ctx.res,{projects:t})}async create(){let t=this.serverServices,r=A(this.ctx),s=await W(this.ctx.req),n=await t.projectStore.projects.create(r,{title:s.title,files:s.files});await b(n,t);let i={project:n};n.files&&(i.files=n.files),n.warnings&&n.warnings.length>0&&(i.warnings=n.warnings),this.ctx.respond=!1,m(this.ctx.res,i)}async remove(){let t=this.serverServices,r=A(this.ctx),s=this.ctx.state.project;if(this.ctx.respond=!1,t.runtime.runSessions.has(s.id)){S(this.ctx.res,409,"project_busy","\u9879\u76EE\u5B58\u5728\u6D3B\u8DC3\u7684\u8FD0\u884C\u4F1A\u8BDD\uFF0C\u65E0\u6CD5\u5220\u9664");return}await t.projectStore.projects.delete(s,r),await Ie(s),await jt(t,s.id),m(this.ctx.res,{ok:!0,id:s.id})}async updateMeta(){let t=this.serverServices,r=A(this.ctx),s=await W(this.ctx.req),n=await t.projectStore.projects.updateMeta(this.ctx.state.project,{title:s.title},r);this.ctx.respond=!1,m(this.ctx.res,{project:n})}async init(){let t=this.serverServices,r=this.ctx.state.project;this.ctx.respond=!1,await b(r,t);let s=t.execution;await qt(r.id,t.workspace,t.projectStore.instances,this.ctx.req,this.ctx.res,(n,i)=>s.bindProjectMachine(n,i)),await Se(t,r,"init")}async getEngine(){let t=this.ctx.state.project,r=this.serverServices.execution,s=await r.getProjectMachine(t.id);if(this.ctx.respond=!1,!s){m(this.ctx.res,{machineId:null,machine:null,status:"unbound"});return}let n=r.isMachineOnline(s),a=(r.listMachines()??[]).find(c=>c.machineId===s);m(this.ctx.res,{machineId:s,machine:a??null,status:n?"online":"offline"})}async bindEngine(){let t=await W(this.ctx.req);if(this.ctx.respond=!1,!t.machineId){S(this.ctx.res,422,"INVALID_INPUT","machineId \u4E0D\u80FD\u4E3A\u7A7A");return}await this.serverServices.execution.bindProjectMachine(this.ctx.state.project.id,t.machineId),m(this.ctx.res,{ok:!0,machineId:t.machineId})}async listFiles(){let t=this.ctx.state.project,r=await R(t);this.ctx.respond=!1,m(this.ctx.res,{project:t,files:r})}async replaceFiles(){let t=this.serverServices,r=A(this.ctx),s=this.ctx.state.project,n=await W(this.ctx.req);if(this.ctx.respond=!1,!Array.isArray(n.files)){S(this.ctx.res,422,"INVALID_INPUT","files \u4E0D\u80FD\u4E3A\u7A7A");return}let i=await t.projectStore.projects.replaceFiles(s,n.files,r),a=await b(s,t);m(this.ctx.res,{project:a.project,files:i})}async patchFiles(){let t=this.serverServices,r=A(this.ctx),s=this.ctx.state.project,n=await W(this.ctx.req),i=await t.projectStore.projects.updateFiles(s,{upsert:n.upsert,remove:n.remove},r),a=await b(s,t);this.ctx.respond=!1,m(this.ctx.res,{project:a.project,files:i})}async exportProject(){let t=this.serverServices,r=A(this.ctx),s=this.ctx.state.project,n=await t.projectStore.projects.exportProject(s,r),i=ir(n),a=t.projectStore.workflows;if(a){let c=await Promise.all((await a.list(s.id,r)).map(async u=>{let p=await a.exportWorkflow(s.id,u.id,r);return{workflow:p.workflow,files:p.files}}));this.ctx.respond=!1,m(this.ctx.res,{...i,workflows:c});return}this.ctx.respond=!1,m(this.ctx.res,i)}async validate(){let t=this.ctx.state.project,r=await wo({workDir:t.dir,configPath:re.join(t.dir,"aitable-workflow.config.yml"),workflows:[],workflowPath:re.join(t.dir,"workflow.yml"),recipeDir:re.join(t.dir,"recipes")});await Ht(t,r),this.ctx.respond=!1,m(this.ctx.res,r)}async getWorkflow(){let t=this.ctx.state.project,r=await ho(t.dir),s=await yo(t,this.serverServices.projectStore.workflows,Re(this.ctx.query?.workflowId),A(this.ctx)),n=await yr(s,{envSource:r}).catch(()=>yr(s)),i=re.join(re.dirname(s),"recipes"),a=n.steps.map(c=>({...c,requires_agent:c.recipe?Kt(i,c.recipe):!!c.prompt}));this.ctx.respond=!1,m(this.ctx.res,{workflow:{...n,steps:a}})}async resetWorkflow(){let t=await _t(this.ctx.state.project),r=await R(t);this.ctx.respond=!1,m(this.ctx.res,{project:t,files:r})}async getState(){let t=this.serverServices,r=this.ctx.state.project,s=await t.projectStore.instances.getRecoverableState(r);this.ctx.respond=!1,m(this.ctx.res,dr(s,t.runtime.runSessions.get(r.id)))}async updateTableId(){let t=this.serverServices;this.ctx.respond=!1,await Ut(this.ctx.state.project.id,t.workspace,t.projectStore.instances,this.ctx.req,this.ctx.res)}};o(T,"ProjectsController"),l([wr()],T.prototype,"ctx",2),l([wr()],T.prototype,"serverServices",2),l([he("/")],T.prototype,"list",1),l([Le("/")],T.prototype,"create",1),l([go("/:projectId")],T.prototype,"remove",1),l([it("/:projectId")],T.prototype,"updateMeta",1),l([Le("/:projectId/init")],T.prototype,"init",1),l([he("/:projectId/engine")],T.prototype,"getEngine",1),l([hr("/:projectId/engine")],T.prototype,"bindEngine",1),l([he("/:projectId/files")],T.prototype,"listFiles",1),l([hr("/:projectId/files")],T.prototype,"replaceFiles",1),l([it("/:projectId/files")],T.prototype,"patchFiles",1),l([he("/:projectId/export")],T.prototype,"exportProject",1),l([Le("/:projectId/validate")],T.prototype,"validate",1),l([he("/:projectId/workflow")],T.prototype,"getWorkflow",1),l([Le("/:projectId/reset-workflow")],T.prototype,"resetWorkflow",1),l([he("/:projectId/state")],T.prototype,"getState",1),l([it("/:projectId/table-id")],T.prototype,"updateTableId",1),T=l([mo("/api/projects")],T);async function yo(e,t,r,s){let n=r&&/^[A-Za-z0-9_-]+$/.test(r)?r:null;if(n&&t){let c=await t.resolve(e.id,n,s);return re.join(e.dir,c.templatePath)}let i=await R(e);if(n){let c=`workflows/${n}/workflow.yml`;if(i.some(u=>u.path===c))return re.join(e.dir,c)}if(i.some(c=>c.path==="workflow.yml"))return re.join(e.dir,"workflow.yml");let a=i.find(c=>/^workflows\/[^/]+\/workflow\.ya?ml$/.test(c.path));return re.join(e.dir,a?.path??"workflow.yml")}o(yo,"resolvePreviewWorkflowPath");import vo from"path";import{Controller as Po,Del as ko,Get as vr,Inject as Pr,Patch as To,Post as So,Put as Do}from"@midwayjs/core";import{loadWorkflowTemplate as jo}from"aitable-workflow-core";var N=class{ctx;serverServices;guardNotConfigured(){return this.serverServices.projectStore.workflows?!1:(this.ctx.respond=!1,S(this.ctx.res,501,"WORKFLOWS_NOT_CONFIGURED","\u5DE5\u4F5C\u6D41\u6301\u4E45\u5316\u672A\u914D\u7F6E\uFF08\u9700\u8981 AITABLE_DB_URL\uFF09"),!0)}async list(){let t=this.serverServices,r=this.ctx.state.project;if(!t.projectStore.workflows){this.ctx.respond=!1,m(this.ctx.res,{workflows:await xo(r)});return}let s=A(this.ctx);this.ctx.respond=!1,m(this.ctx.res,{workflows:await t.projectStore.workflows.list(r.id,s)})}async create(){if(this.guardNotConfigured())return;let t=this.serverServices,r=A(this.ctx),s=this.ctx.state.project,n=await W(this.ctx.req);if(!n.name||!n.name.trim()){this.ctx.respond=!1,S(this.ctx.res,422,"INVALID_INPUT","\u5DE5\u4F5C\u6D41\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");return}let i=await t.projectStore.workflows.create(s.id,{name:n.name,templatePath:n.templatePath,files:n.files},r);await b(s,t);let a={workflow:i.workflow};i.files&&(a.files=i.files),i.warnings&&i.warnings.length>0&&(a.warnings=i.warnings),this.ctx.respond=!1,m(this.ctx.res,a)}async get(){let t=this.serverServices,r=this.ctx.state.project,s=this.ctx.params.workflowId??"";if(!t.projectStore.workflows){let a=await Oo(r,s);if(this.ctx.respond=!1,!a){S(this.ctx.res,404,"WORKFLOW_NOT_FOUND","\u5DE5\u4F5C\u6D41\u4E0D\u5B58\u5728");return}m(this.ctx.res,a);return}let n=A(this.ctx),i=await t.projectStore.workflows.exportWorkflow(r.id,s,n);this.ctx.respond=!1,m(this.ctx.res,{workflow:i.workflow,files:i.files})}async remove(){if(this.guardNotConfigured())return;let t=this.serverServices,r=A(this.ctx),s=this.ctx.state.project;if(t.runtime.runSessions.has(s.id)){this.ctx.respond=!1,S(this.ctx.res,409,"project_busy","\u9879\u76EE\u5B58\u5728\u6D3B\u8DC3\u7684\u8FD0\u884C\u4F1A\u8BDD\uFF0C\u65E0\u6CD5\u5220\u9664\u5DE5\u4F5C\u6D41");return}let n=await t.projectStore.workflows.resolve(s.id,this.ctx.params.workflowId??"",r);await t.projectStore.workflows.delete(n,r),await b(s,t),this.ctx.respond=!1,m(this.ctx.res,{ok:!0,id:n.id})}async replaceFiles(){if(this.guardNotConfigured())return;let t=this.serverServices,r=A(this.ctx),s=this.ctx.state.project,n=await t.projectStore.workflows.resolve(s.id,this.ctx.params.workflowId??"",r),i=await W(this.ctx.req);if(!Array.isArray(i.files)){this.ctx.respond=!1,S(this.ctx.res,422,"INVALID_INPUT","files \u4E0D\u80FD\u4E3A\u7A7A");return}let a=await t.projectStore.workflows.replaceFiles(n,i.files,r);await b(s,t),this.ctx.respond=!1,m(this.ctx.res,{workflow:n,files:a})}async updateFiles(){if(this.guardNotConfigured())return;let t=this.serverServices,r=A(this.ctx),s=this.ctx.state.project,n=await t.projectStore.workflows.resolve(s.id,this.ctx.params.workflowId??"",r),i=await W(this.ctx.req),a=await t.projectStore.workflows.updateFiles(n,{upsert:i.upsert,remove:i.remove},r);await b(s,t),this.ctx.respond=!1,m(this.ctx.res,{workflow:n,files:a})}};o(N,"WorkflowsController"),l([Pr()],N.prototype,"ctx",2),l([Pr()],N.prototype,"serverServices",2),l([vr("/:projectId/workflows")],N.prototype,"list",1),l([So("/:projectId/workflows")],N.prototype,"create",1),l([vr("/:projectId/workflows/:workflowId")],N.prototype,"get",1),l([ko("/:projectId/workflows/:workflowId")],N.prototype,"remove",1),l([Do("/:projectId/workflows/:workflowId/files")],N.prototype,"replaceFiles",1),l([To("/:projectId/workflows/:workflowId/files")],N.prototype,"updateFiles",1),N=l([Po("/api/projects")],N);async function xo(e){let t=await R(e),r=kr(t);return(await Promise.all(r.map(n=>jr(e,n,t)))).sort((n,i)=>n.name.localeCompare(i.name))}o(xo,"listLocalWorkflows");async function Oo(e,t){let r=await R(e),s=kr(r).find(c=>Tr(c)===t);if(!s)return null;let n=await jr(e,s,r),i=Sr(s),a=r.filter(c=>Dr(c.path,s)).map(c=>({...c,path:c.path.slice(i.length)}));return{workflow:n,files:a}}o(Oo,"exportLocalWorkflow");function kr(e){return e.map(t=>t.path).filter(t=>t==="workflow.yml"||/^workflows\/[^/]+\/workflow\.ya?ml$/.test(t))}o(kr,"localWorkflowTemplatePaths");function Tr(e){return e==="workflow.yml"?"main":e.split("/")[1]??"main"}o(Tr,"localWorkflowId");function Sr(e){return e==="workflow.yml"?"":e.replace(/workflow\.ya?ml$/,"")}o(Sr,"workflowBase");function Dr(e,t){let r=Sr(t);return r?e.startsWith(r):e==="workflow.yml"||e==="views.yml"||e.startsWith("recipes/")}o(Dr,"belongsToWorkflow");async function jr(e,t,r){let s=Tr(t),n=await jo(vo.join(e.dir,t)).catch(a=>(console.warn(`\u52A0\u8F7D\u672C\u5730\u5DE5\u4F5C\u6D41\u6A21\u677F\u5931\u8D25 [${t}]\uFF1A`,a instanceof Error?a.message:String(a)),null)),i=r.filter(a=>Dr(a.path,t)).length;return{id:s,projectId:e.id,name:n?.meta.name??s,domain:n?.meta.domain,templatePath:t,tableId:n?.tracker?.table_id,fileCount:i,createdAt:e.createdAt,updatedAt:e.updatedAt}}o(jr,"buildLocalWorkflow");import{Controller as fn,Get as Wr,Inject as Nr,Post as ne}from"@midwayjs/core";function Ro(){let e=process.env.AITABLE_WORKFLOW_SSE_HEARTBEAT_MS,t=typeof e=="string"?e.trim():"";if(!t)return 15e3;let r=Number.parseInt(t,10);return Number.isFinite(r)&&r>0?r:15e3}o(Ro,"readSseHeartbeatMs");function Q(e){e.writeHead(200,{"content-type":"text/event-stream; charset=utf-8","cache-control":"no-cache, no-transform",connection:"keep-alive"}),bo(e)}o(Q,"startSse");function bo(e){let t=Ro(),r=setInterval(()=>{if(e.writableEnded||!e.writable){clearInterval(r);return}try{e.write(`:keepalive
13
11
 
14
- `)}catch{clearInterval(r)}},t);r.unref?.();let o=s(()=>clearInterval(r),"stop");e.on("close",o),e.on("finish",o)}s(Wo,"startSseHeartbeat");function G(e,t,r){e.write(`event: ${t}
12
+ `)}catch{clearInterval(r)}},t);r.unref?.();let s=o(()=>clearInterval(r),"stop");e.on("close",s),e.on("finish",s)}o(bo,"startSseHeartbeat");function se(e,t,r){e.write(`event: ${t}
15
13
  `),e.write(`data: ${JSON.stringify(r)}
16
14
 
17
- `)}s(G,"writeSse");var Ue=class{constructor(t){this.services=t}services;static{s(this,"LocalRunController")}mode="local";async start(t,r){z(r),await zt(t,this.services.workspace,this.services.projectStore.instances,this.services.runtime,r,G)}async stop(t){let r=this.services.runtime.runSessions.get(t);if(!r)return{mode:this.mode,running:!1,stopped:!1};if(!r.app)throw Object.assign(new Error("\u8FD0\u884C\u4F1A\u8BDD\u6B63\u5728\u542F\u52A8\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"),{code:"RUN_STARTING",status:409});return await r.app.stop(),{mode:this.mode,running:!1,stopped:!0,taskId:r.taskId}}async status(t){let r=this.services.runtime.runSessions.get(t);return{mode:this.mode,running:r!=null,taskId:r?.taskId??null}}async listRecords(t,r){let o=await R(this.services.workspace,t),i=(await this.services.projectStore.instances.getState(o)).runSnapshot?.records??[],a=r?.limit??100;return{items:[...i].sort((u,p)=>p.updatedAt-u.updatedAt).slice(0,a).map(u=>({itemId:u.id,title:u.title,state:u.state,currentStepId:u.currentStepId,doneStepIds:u.doneStepIds,completedAt:u.status==="done"?new Date(u.updatedAt).toISOString():null,updatedAt:new Date(u.updatedAt).toISOString(),status:u.status})),counts:{total:i.length,completed:i.filter(u=>u.status==="done").length}}}},ze=class{constructor(t,r){this.scheduler=t;this.records=r}scheduler;records;static{s(this,"LeaseRunController")}mode="lease";async start(t,r){_(r,{mode:this.mode,action:"start",schedule:await this.scheduler.start(t)})}async stop(t){return{mode:this.mode,action:"stop",schedule:await this.scheduler.stop(t)}}async status(t){return{mode:this.mode,action:"status",schedule:await this.scheduler.getStatus(t)}}async listRecords(t,r){return this.records?this.records.listRecords(t,r):{items:[],counts:{total:0,completed:0}}}};function ce(e){let{runScheduler:t,runRecords:r}=e.execution;return t?new ze(t,r):new Ue(e)}s(ce,"resolveRunController");import Co from"path";import{collectDesignContext as sr,runDesign as No,runClarifyRound as Mo,syncFieldsFromWorkflow as Lo,syncViewsFromDesign as Bo,summarizeForms as $o,validateFormAnswers as Ho,pruneHiddenAnswers as qo,logger as or}from"aitable-workflow-core";var De=class{static{s(this,"DesignEventBuffer")}capacity;events=[];seq=0;constructor(t=100){this.capacity=t>0?t:100}push(t){this.seq+=1;let r={seq:this.seq,event:t};return this.events.push(r),this.events.length>this.capacity&&this.events.shift(),r}get cursor(){return this.seq}get size(){return this.events.length}replayAfter(t){return t===void 0||Number.isNaN(t)?[...this.events]:this.events.filter(r=>r.seq>t)}};async function ir(e,t,r,o,n){let i=await R(t,e),a=await r.getState(i),c=a.stages.design?.task,u=c?.status==="failed"?c:null;if(u){await r.clearActiveTask(i,u.id),_(o,{task:u,error:{code:"DESIGN_RETRY_REQUIRED",message:u.error?.message??"\u4E0A\u4E00\u6B21 Design \u5DF2\u5931\u8D25\uFF0C\u8BF7\u70B9\u51FB\u91CD\u65B0\u751F\u6210\u5DE5\u4F5C\u6D41\u8BBE\u8BA1\u3002",retryable:!0}},409);return}let p=N(a),l=p?.stage==="design"?p:null,f=l||await r.createTask(i,"design");try{let h=B(i),k=await sr({configPath:h,yes:!0}),d=k.agentType?await Mo(k,{...await ar(n,i,k.agentType),timeoutMs:k.agentTimeoutMs,interactive:!0,judgeOnly:!0}):void 0;if(!d?.form){let A=k.agentType?"Agent \u5224\u65AD\u5F53\u524D\u9700\u6C42\u5DF2\u8DB3\u591F\u660E\u786E\uFF0C\u672A\u63D0\u51FA\u6F84\u6E05\u95EE\u9898\uFF0C\u76F4\u63A5\u5F00\u59CB\u751F\u6210\u3002":"\u5F53\u524D\u5B9E\u4F8B\u672A\u914D\u7F6E coding agent\uFF0C\u8DF3\u8FC7\u9700\u6C42\u6F84\u6E05\u3002",Z={form:{id:"clarify",title:"\u65E0\u9700\u6F84\u6E05",questions:[]},answers:{},summary:A,clarifications:A,confirmed:!0,...d?.sessionId?{agentSessionId:d.sessionId}:{}},X=await r.updateTask(i,f.id,{status:"running",checkpoint:"discovery_confirmed",currentAction:"\u9700\u6C42\u5DF2\u660E\u786E\uFF0C\u51C6\u5907\u751F\u6210\u5DE5\u4F5C\u6D41\u65B9\u6848",block:void 0,error:void 0,outputs:{...f.outputs,discovery:Z,clarifications:A}});_(o,{task:X,discovery:Z});return}let v={form:d.form,answers:Uo(d.form),summary:d.form.description??d.form.title,confirmed:!1,...d.sessionId?{agentSessionId:d.sessionId}:{}},y=await r.updateTask(i,f.id,{status:"blocked",checkpoint:"discovery_waiting_user",currentAction:"\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u9700\u6C42\u6F84\u6E05",block:{reason:"discovery_required",message:"\u8BF7\u5148\u786E\u8BA4 Design \u9636\u6BB5\u7684\u9700\u6C42\u6F84\u6E05\u5361\u7247\uFF0C\u518D\u5F00\u59CB\u751F\u6210\u5DE5\u4F5C\u6D41\u65B9\u6848\u3002",actionLabel:"\u786E\u8BA4\u540E\u5F00\u59CB\u751F\u6210"},outputs:{...f.outputs,discovery:v}});_(o,{task:y,discovery:v})}catch(h){let k=await r.updateTask(i,f.id,{status:"failed",checkpoint:"discovery_precheck",currentAction:"\u9700\u6C42\u6F84\u6E05\u51C6\u5907\u5931\u8D25",error:{code:"DESIGN_PRECHECK_FAILED",message:T(h),retryable:!0,details:M(h)}});_(o,{task:k,error:ne(h)},500)}}s(ir,"handleDesignDiscovery");async function ar(e,t,r){let o=await e.buildDesignExecutor({projectId:t.id,agent:r,projectDir:t.dir});return o?{executor:o}:{}}s(ar,"relayExecutorFor");function Uo(e){let t=Object.fromEntries(e.questions.map(r=>[r.id,r.defaultValue??(r.type==="checkbox"?[]:"")]));return qo(e,t)}s(Uo,"seedAnswers");async function cr(e,t,r,o,n){let i=await R(t,e),a=await r.getState(i),c=N(a),u=c?.stage==="design"?c:a.stages.design?.task,p=u?.outputs?.discovery;if(!u||!p){q(n,409,"DISCOVERY_NOT_READY","\u8BF7\u5148\u751F\u6210\u9700\u6C42\u6F84\u6E05\u5361\u7247\uFF0C\u518D\u786E\u8BA4 Design \u8F93\u5165\u3002");return}let l=o.answers,f=l&&Object.keys(l).length>0?l:p.answers,h=Ho(p.form,f);if(h.length>0){q(n,400,"DISCOVERY_INVALID_ANSWERS",`\u9700\u6C42\u6F84\u6E05\u7B54\u6848\u4E0D\u5B8C\u6574\uFF1A${h.map(y=>y.message).join("\uFF1B")}`,{issues:h});return}let k=$o([{form:p.form,answers:f}]),d={...p,answers:f,clarifications:k,summary:k.replace(/\s+/g," ").trim(),confirmed:!0},v=await r.updateTask(i,u.id,{status:"running",checkpoint:"discovery_confirmed",currentAction:"\u9700\u6C42\u6F84\u6E05\u5DF2\u786E\u8BA4\uFF0C\u51C6\u5907\u751F\u6210\u5DE5\u4F5C\u6D41\u65B9\u6848",block:void 0,error:void 0,outputs:{...u.outputs,discovery:d,clarifications:k}});_(n,{task:v,discovery:d})}s(cr,"handleDesignDiscoveryConfirm");async function ur(e,t,r,o,n,i){let a=await R(t,e);try{n.ensureAvailable()}catch(g){q(o,se(g),C(g),T(g));return}let c=await r.getState(a),u=N(c),p=u?.stage==="design"?u:null;if(p&&p.status==="blocked"){z(o),G(o,"task.blocked",{type:"task.blocked",task:p}),o.end();return}if(ie.has(a.id)){z(o);let g=le.get(a.id);if(g){for(let P of g.buffer.replayAfter(i))Ce(o,P.seq,P.event);g.subscribers.add(o),o.on("close",()=>g.subscribers.delete(o));return}p&&G(o,"task.snapshot",{type:"task.snapshot",task:p}),o.end();return}let l=c.stages.design?.task,f=l?.outputs?.discovery,h=f?.confirmed&&typeof l?.outputs?.clarifications=="string";if(p&&p.status==="running"&&!h){z(o),G(o,"task.snapshot",{type:"task.snapshot",task:p}),o.end();return}let k=l?.status==="failed"&&f?.confirmed&&typeof l.outputs?.clarifications=="string",d=p&&h?p:k?await r.updateTask(a,(await r.createTask(a,"design")).id,{checkpoint:"discovery_confirmed",currentAction:"\u91CD\u65B0\u751F\u6210\u5DE5\u4F5C\u6D41\u8BBE\u8BA1",outputs:{discovery:f,clarifications:l.outputs?.clarifications}}):await r.createTask(a,"design");ie.add(a.id),z(o);let v={buffer:new De,subscribers:new Set([o])};le.set(a.id,v),o.on("close",()=>v.subscribers.delete(o));let y=s(g=>ot(v,g),"send"),A=Date.now(),Z=A,X=0,ee=[],J=[],$=s((g,P,j)=>{let W=Date.now(),oe=W-A,E=W-Z;Z=W,ee.push({phase:g,message:P,elapsedMs:oe,deltaMs:E,detail:j}),or.info("design-runtime",P,{projectId:a.id,taskId:d.id,phase:g,elapsedMs:oe,deltaMs:E,...j?{detail:j}:{}}),y({type:"design.progress",phase:g,message:P,elapsedMs:oe,deltaMs:E,detail:j})},"emitProgress"),b=s(async(g,P,j)=>{$(g,P,j);let W=await r.updateTask(a,d.id,{currentAction:P,checkpoint:g});y({type:"task.progress",taskId:W.id,message:P,checkpoint:g})},"progress"),ye="",pe=!1,de=!1,Y=s(async()=>{if(pe){de=!0;return}pe=!0;try{do{de=!1;let P=(await D(a)).filter(j=>j.path!=="project.json").map(j=>`${j.path}:${j.content}`).join("|");if(P&&P!==ye){ye=P;let j=await x(a),W=await D(j);$("file_snapshot",`\u540C\u6B65 ${W.length} \u4E2A\u751F\u6210\u6587\u4EF6`,{fileCount:W.length}),y({type:"design.files",files:W})}}while(de)}finally{pe=!1}},"sendFileSnapshot");try{y({type:"task.started",task:d}),y({type:"design.started",projectId:e}),await Y();let g,P;try{await b("precheck","\u8BFB\u53D6 Design \u914D\u7F6E"),P=B(a),g=await sr({configPath:P,yes:!0}),await b("precheck_done","Design \u914D\u7F6E\u8BFB\u53D6\u5B8C\u6210",{agent:g.agentType,timeoutMs:g.agentTimeoutMs})}catch(m){let H=await r.updateTask(a,d.id,{status:"failed",checkpoint:"precheck",currentAction:"Design \u9884\u68C0\u67E5\u5931\u8D25",error:{code:"DESIGN_PRECHECK_FAILED",message:T(m),retryable:!0,details:M(m)}});y({type:"task.failed",task:H}),y({type:"error",error:ne(m)}),o.end();return}let j=d.outputs?.discovery,W=typeof d.outputs?.clarifications=="string"?d.outputs.clarifications:j?.clarifications;if(!j?.confirmed||!W){await b("waiting_input","\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u9700\u6C42\u6F84\u6E05");let m=await r.updateTask(a,d.id,{status:"blocked",checkpoint:"discovery_waiting_user",currentAction:"\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u9700\u6C42\u6F84\u6E05",block:{reason:"discovery_required",message:"\u8BF7\u5148\u786E\u8BA4 Design \u9636\u6BB5\u7684\u9700\u6C42\u6F84\u6E05\u5361\u7247\uFF0C\u518D\u5F00\u59CB\u751F\u6210\u5DE5\u4F5C\u6D41\u65B9\u6848\u3002",actionLabel:"\u786E\u8BA4\u540E\u5F00\u59CB\u751F\u6210"},outputs:d.outputs});y({type:"task.blocked",task:m}),o.end();return}g.clarifications=W,await b("brief_ready","\u9700\u6C42\u6458\u8981\u5DF2\u786E\u8BA4\uFF0C\u51C6\u5907\u542F\u52A8 Agent"),y({type:"design.discovery",summary:j.summary}),await b("agent_start","\u542F\u52A8 Coding Agent \u751F\u6210\u6D41\u7A0B\u65B9\u6848",{agent:g.agentType,timeoutMs:null,timeoutSource:"disabled"});let oe=!1,E=await No(g,{...await ar(n,a,g.agentType),...j.agentSessionId?{resume:{sessionId:j.agentSessionId,answered:[{form:j.form,answers:j.answers}]}}:{},timeoutMs:null,onRoundStart:s((m,H)=>{$("agent_round",`\u7B2C ${m} \u8F6E ${H?"\u4FEE\u6B63":"\u751F\u6210"}\u5F00\u59CB`,{round:m,isFix:H}),y({type:"design.round_start",round:m,isFix:H})},"onRoundStart"),onEvent:s(m=>{zo(m)&&(J.push(Jo(m)),J.length>30&&J.shift(),oe||(oe=!0,$("agent_first_event",`\u6536\u5230 Agent \u9996\u4E2A\u4E8B\u4EF6\uFF1A${m.type}`,{eventType:m.type})),m.type==="tool_use"&&$("agent_tool","Agent \u8C03\u7528\u5DE5\u5177",{toolName:m.toolName}),(m.type==="text"||m.type==="thinking")&&Date.now()-X>5e3&&(X=Date.now(),$("agent_output",m.type==="thinking"?"Agent \u6B63\u5728\u63A8\u7406":"Agent \u8F93\u51FA\u5185\u5BB9",{eventType:m.type,preview:m.content?.replace(/\s+/g," ").slice(0,120)})),y({type:"design.agent_event",event:m}),(m.type==="tool_use"||m.type==="tool_result")&&Y())},"onEvent"),onAgentLifecycle:s(m=>{$(Ko(m.phase),Go(m),Vo(m))},"onAgentLifecycle"),onValidation:s((m,H)=>{$("validating",m.valid?"\u6D41\u7A0B\u65B9\u6848\u6821\u9A8C\u901A\u8FC7":`\u6D41\u7A0B\u65B9\u6848\u6821\u9A8C\u53D1\u73B0 ${m.errors.length} \u4E2A\u95EE\u9898`,{round:H,valid:m.valid,errorCount:m.errors.length}),y({type:"design.validation",round:H,validation:m}),Y()},"onValidation")});if(!E.success){await Y();let m=await r.updateTask(a,d.id,{status:"failed",checkpoint:"validating",currentAction:"\u771F\u5B9E\u8BBE\u8BA1\u672A\u901A\u8FC7\u6821\u9A8C",error:{code:"DESIGN_VALIDATION_FAILED",message:E.response,retryable:!0},outputs:{...d.outputs,diagnostics:Je(A,ee,J,E.validation)}});await nr(a,m,E.response),y({type:"task.failed",task:m}),y({type:"design.done",success:!1,rounds:E.rounds,response:E.response}),o.end();return}await b("sync_fields","\u540C\u6B65\u5B57\u6BB5\u8BA1\u5212\u5230\u914D\u7F6E");for(let m of g.workflows)await Lo({workflowPath:m.workflowPath,configPath:P,autoUpdate:!0}).catch(H=>(or.warn("design","\u5B57\u6BB5\u540C\u6B65\u5931\u8D25(\u4E0D\u963B\u65AD)",{workflow:m.id,error:T(H)}),null));await b("sync_views","\u540C\u6B65\u89C6\u56FE\u8BA1\u5212\u5230\u914D\u7F6E"),await Bo({viewsPath:Co.resolve(g.workDir,"views.yml"),configPath:P,autoUpdate:!0}).catch(()=>null),await Y(),await b("manifest","\u5199\u5165 Design \u4EA7\u7269\u6E05\u5355");let br=await D(a),Qe=Yo(a,d,br);await Be(a,Qe);let Ir=await r.updateTask(a,d.id,{status:"succeeded",currentAction:"\u5DE5\u4F5C\u6D41\u8BBE\u8BA1\u5B8C\u6210",checkpoint:"done",outputs:{...d.outputs,manifest:Qe,diagnostics:Je(A,ee,J,E.validation)}});y({type:"task.succeeded",task:Ir}),y({type:"design.done",success:!0,rounds:E.rounds,response:E.response}),o.end()}catch(g){await Y().catch(()=>{});let P=await r.updateTask(a,d.id,{status:"failed",checkpoint:"generating",currentAction:"\u771F\u5B9E\u8BBE\u8BA1\u751F\u6210\u5931\u8D25",error:{code:C(g)==="INTERNAL_ERROR"?"DESIGN_GENERATION_FAILED":C(g),message:T(g),retryable:!0,details:M(g)},outputs:{...d.outputs,diagnostics:Je(A,ee,J)}});await nr(a,P,T(g)),y({type:"task.failed",task:P}),y({type:"error",error:ne(g)}),y({type:"design.done",success:!1,rounds:0,response:T(g)}),o.end()}finally{nt(a.id)}}s(ur,"handleDesign");function zo(e){return e.type!=="text"&&e.type!=="thinking"?!0:typeof e.content=="string"&&e.content.trim().length>0}s(zo,"isDisplayableAgentEvent");function Jo(e){return e.type==="tool_use"?{type:e.type,toolName:e.toolName,toolInput:e.toolInput}:e.type==="tool_result"||e.type==="error"||e.type==="text"||e.type==="thinking"?{type:e.type,content:e.content?.replace(/\s+/g," ").slice(0,240)}:{type:e.type}}s(Jo,"summarizeAgentEvent");function Ko(e){return`agent_${e}`}s(Ko,"agentLifecyclePhase");function Go(e){switch(e.phase){case"process_spawned":return"Coding Agent \u8FDB\u7A0B\u5DF2\u542F\u52A8";case"first_stdout":return"Coding Agent \u9996\u6B21\u8F93\u51FA stdout";case"first_stderr":return"Coding Agent \u9996\u6B21\u8F93\u51FA stderr";case"process_closed":return"Coding Agent \u8FDB\u7A0B\u5DF2\u9000\u51FA"}}s(Go,"agentLifecycleMessage");function Vo(e){return{provider:e.provider,phase:e.phase,elapsedMs:e.elapsedMs,...e.pid?{pid:e.pid}:{},...e.cmd?{cmd:e.cmd}:{},...e.cwd?{cwd:e.cwd}:{},...e.exitCode!==void 0?{exitCode:e.exitCode}:{}}}s(Vo,"sanitizeAgentLifecycle");function Je(e,t,r,o){return{elapsedMs:Date.now()-e,timeline:t.slice(-40),recentAgentEvents:r,validation:o}}s(Je,"buildDesignDiagnostics");function Yo(e,t,r){let o=r.filter(a=>Qo(a.path)).map(a=>({path:a.path,kind:pr(a.path),checksum:me(a.content),required:Zo(a.path)})),n=o.filter(a=>a.required).map(a=>a.path),i=new Date().toISOString();return{projectId:e.id,taskId:t.id,designVersion:t.attempt,status:"ready",expected:n,generated:o,validation:{passed:!0,errors:[],warnings:[]},createdAt:t.startedAt,updatedAt:i}}s(Yo,"buildDesignManifest");function Qo(e){return e==="workflow.yml"||e==="views.yml"||e.startsWith("recipes/")||/^workflows\/[^/]+\/workflow\.ya?ml$/.test(e)||/^workflows\/[^/]+\/views\.ya?ml$/.test(e)||/^workflows\/[^/]+\/recipes\/.+\.(ts|js)$/.test(e)}s(Qo,"isDesignArtifactPath");function pr(e){return e.endsWith("workflow.yml")||e.endsWith("workflow.yaml")?"workflow":e.endsWith("views.yml")||e.endsWith("views.yaml")?"views":"recipe"}s(pr,"designArtifactKind");function Zo(e){return pr(e)!=="views"}s(Zo,"isRequiredDesignArtifact");async function nr(e,t,r){let o=new Date().toISOString();await Be(e,{projectId:e.id,taskId:t.id,designVersion:t.attempt,status:"invalid",expected:["workflow.yml"],generated:[],validation:{passed:!1,errors:[r],warnings:[]},createdAt:t.startedAt,updatedAt:o})}s(nr,"writeInvalidDesignManifest");import an from"path";import{loadSettings as cn,resolveConfigPath as un,loadWorkflowTemplate as pn,initTable as dn,ensureTableExists as ln,syncViewsFromDesign as fn,runWithExecutor as mn,runWithDwsRunner as gn,DwsError as wn,logger as yn}from"aitable-workflow-core";var Xo={singleSelect:"single_select",multipleSelect:"multiple_select",date:"datetime"},en={Grid:"grid",FormDesigner:"form",Kanban:"kanban",Calendar:"calendar",Gantt:"gantt"};function dr(e,t){let r=new Map,o=new Map((e.tracker.field_definitions??[]).map(c=>[c.name,c])),n=s((c,u,p="text",l)=>{if(!c?.trim()||r.has(c))return;let f=o.get(c);r.set(c,{id:nn(c),name:c,type:on(f?.type??p),...f?.options??l?{options:f?.options??l}:{},group:u})},"addField");for(let c of t.referencedFields.userInput)n(c,"business");for(let c of t.referencedFields.output)n(c,"business");for(let c of t.referencedFields.other)n(c,"business");for(let c of e.tracker.field_definitions??[])n(c.name,"business",c.type,c.options);n(e.tracker.fields.title,"business"),n(e.tracker.fields.description,"business"),n(e.tracker.fields.state,"process","singleSelect",rn(e)),n(e.tracker.fields.current_step,"process"),n(e.tracker.fields.owner,"process","user"),n(e.tracker.fields.deadline,"process","date"),n(e.tracker.fields.event_log,"process");let i=tn(e.tracker.views??[],t).map(c=>({id:c.id,name:c.name,kind:en[c.type]??"grid",desc:c.description??sn(c)})),a=[...r.values()];return{fieldCount:a.length,viewCount:i.length,fields:a,views:i}}s(dr,"buildSetupSchemaSummary");function tn(e,t){let r=[],o=2;for(let n of e){if(!n.for_each_step){r.push(n);continue}let i=t.steps.filter(a=>n.for_each_step==="needs_human"?!!a.needs_human:!0);for(let a of i){let c=String.fromCodePoint(9311+o);r.push({...n,id:`step_${a.id}`,name:`${c} ${a.name}`,visible_fields:[...Object.values(a.input_mapping??{}),...Object.values(a.output_mapping??{})],description:a.needs_human?.reason??n.description,for_each_step:void 0}),o++}}return r}s(tn,"expandViews");function rn(e){return[e.tracker.states.initial,...e.tracker.states.active,...e.tracker.states.terminal,e.tracker.states.blocked,e.tracker.states.review].filter((t,r,o)=>!!t&&o.indexOf(t)===r)}s(rn,"stateOptions");function on(e){return Xo[e]??e}s(on,"displayFieldType");function nn(e){return`fld_${e.replace(/[^\p{L}\p{N}]+/gu,"_").replace(/^_+|_+$/g,"")||"field"}`}s(nn,"fieldId");function sn(e){return e.type==="FormDesigner"?"\u8868\u5355\u5F55\u5165\u89C6\u56FE":e.type==="Kanban"?e.group_by?`\u6309 ${e.group_by} \u5206\u7EC4`:"\u770B\u677F\u89C6\u56FE":e.type==="Calendar"?e.date_field?`\u6309 ${e.date_field} \u67E5\u770B`:"\u65E5\u5386\u89C6\u56FE":e.type==="Gantt"?"\u7518\u7279\u89C6\u56FE":"\u8868\u683C\u89C6\u56FE"}s(sn,"viewDescription");async function Ke(e,t,r,o,n){let i=await R(t,e);try{n.ensureAvailable()}catch(d){q(o,se(d),C(d),T(d));return}let a=await r.getState(i),c=N(a),u=c?.stage==="setup"?c:null;if(u?.status==="running"){z(o),G(o,"task.snapshot",{type:"task.snapshot",task:u}),o.end();return}let p=u?.status==="blocked"?await r.updateTask(i,u.id,{status:"running",currentAction:"\u91CD\u65B0\u68C0\u67E5\u6388\u6743\u5E76\u7EE7\u7EED\u642D\u5EFA",block:void 0,error:void 0}):await r.createTask(i,"setup");z(o);let l=s(d=>G(o,d.type,d),"send"),f=s(async(d,v)=>{let y=await r.updateTask(i,p.id,{currentAction:d,checkpoint:v});l({type:"task.progress",taskId:y.id,message:d,checkpoint:v}),l({type:"setup.progress",message:d})},"progress"),h=n.buildSetupExecutor({projectId:i.id,projectDir:i.dir}),k=n.buildDwsRunner();try{await mn(h,async()=>{await gn(k,async()=>{l({type:"task.started",task:p}),l({type:"setup.started",projectId:e}),await f("\u52A0\u8F7D\u914D\u7F6E\u4E0E\u5DE5\u4F5C\u6D41\u6A21\u677F","preflight"),await fn({viewsPath:an.resolve(i.dir,"views.yml"),configPath:B(i),autoUpdate:!0}).catch(b=>(yn.warn("setup","setup \u524D\u540C\u6B65 views.yml \u5931\u8D25\uFF0C\u7EE7\u7EED\u4F7F\u7528 config \u4E2D\u7684\u89C6\u56FE\u914D\u7F6E",{error:T(b)}),null));let d=await cn(B(i),{lenientEnv:!0}),v=await pn(d.workflowTemplatePath,{envSource:d.envSource}),y=d.tracker.base_id==="__placeholder__"?void 0:d.tracker,A=y??v.tracker,Z=!!A&&!A.table_id.trim();if(A&&!A.table_id.trim()){let b=y?await un(B(i)):d.workflowTemplatePath,ye=y?d:{...d,tracker:A},pe=await ln({trackerFilePath:b,settings:ye,workflow:v,onProgress:s((de,Y)=>f(de,Y),"onProgress")});d={...d,tracker:pe.tracker}}await f("\u5F00\u59CB\u521B\u5EFA\u5B57\u6BB5\u548C\u89C6\u56FE","sync_schema");let X=await dn({settings:d,workflow:v,cleanEmptyRows:!0,tableJustCreated:Z});for(let b of X.warnings)await f(`\u6CE8\u610F\uFF1A${b.message}`,"sync_schema");let ee=dr(d,v),J=X.warnings.map(b=>b.message);await x(i);let $=await r.updateTask(i,p.id,{status:"succeeded",currentAction:"AI \u8868\u683C\u642D\u5EFA\u5B8C\u6210",checkpoint:"done",outputs:{tableUrl:$e(d.tracker.base_id,d.tracker.table_id),baseId:d.tracker.base_id,tableId:d.tracker.table_id,schema:ee,warnings:J}});l({type:"task.succeeded",task:$}),l({type:"setup.done",tableUrl:$e(d.tracker.base_id,d.tracker.table_id),baseId:d.tracker.base_id,tableId:d.tracker.table_id,schema:ee,warnings:J}),o.end()})})}catch(d){if(hn(d)){let y=T(d),A=await r.updateTask(i,p.id,{status:"blocked",checkpoint:"preflight",currentAction:"\u7B49\u5F85 AI \u8868\u683C\u6388\u6743",block:{reason:"auth_required",message:`\u6388\u6743\u6216\u6743\u9650\u4E0D\u8DB3\uFF1A${y}`,actionLabel:"\u6211\u5DF2\u5B8C\u6210\u6388\u6743\uFF0C\u7EE7\u7EED\u642D\u5EFA",help:"\u8BF7\u6267\u884C dws auth login\uFF0C\u5E76\u786E\u8BA4\u5F53\u524D\u8D26\u53F7\u53EF\u8BBF\u95EE\u76EE\u6807 Base/Table\u3002"}});l({type:"task.blocked",task:A}),o.end();return}let v=await r.updateTask(i,p.id,{status:"failed",currentAction:"AI \u8868\u683C\u642D\u5EFA\u5931\u8D25",error:{code:C(d),message:T(d),retryable:!0,details:M(d)}});l({type:"task.failed",task:v}),l({type:"error",error:ne(d)}),o.end()}}s(Ke,"handleSetup");function hn(e){let t=T(e).toLowerCase(),r=e instanceof wn?e.stderr.toLowerCase():"",o=`${t} ${r}`;return["auth","login","permission","unauthorized","forbidden","access denied"].some(n=>o.includes(n))}s(hn,"isSetupAuthBlocker");function lr(e){e.post("/:projectId/tasks/:stage/:taskAction",L(async t=>{let{services:r,outgoing:o}=t.env,n=t.env.project,i=t.req.param("stage")??"",a=t.req.param("taskAction")??"",c=a==="start"||a==="continue"||a==="retry";if(c&&i==="setup"){await O(n,r),await Ke(n.id,r.workspace,r.projectStore.instances,o,K(t));return}if(c&&i==="run"){await O(n,r),await ce(r).start(n.id,o);return}q(o,404,"NOT_FOUND","\u63A5\u53E3\u4E0D\u5B58\u5728")})),e.post("/:projectId/design/discovery",L(async t=>{let{services:r,outgoing:o}=t.env,n=t.env.project;await O(n,r),await ir(n.id,r.workspace,r.projectStore.instances,o,K(t))})),e.post("/:projectId/design/discovery/confirm",L(async t=>{let{services:r,incoming:o,outgoing:n}=t.env,i=t.env.project;await O(i,r);let a=await I(o);await cr(i.id,r.workspace,r.projectStore.instances,a,n)})),e.post("/:projectId/design",L(async t=>{let{services:r,incoming:o,outgoing:n}=t.env,i=t.env.project;await O(i,r);let a=new URL(t.req.url),c=st(a,o);await ur(i.id,r.workspace,r.projectStore.instances,n,K(t),c),await fe(r,i,"design")})),e.post("/:projectId/setup",L(async t=>{let{services:r,outgoing:o}=t.env,n=t.env.project;await O(n,r),await Ke(n.id,r.workspace,r.projectStore.instances,o,K(t)),await fe(r,n,"setup")})),e.post("/:projectId/run/records/:recordId/human-action",L(async t=>{let{services:r,incoming:o,outgoing:n}=t.env,i=t.env.project;await O(i,r),await Dt(i.id,r.workspace,r.projectStore.instances,r.runtime,t.req.param("recordId")??"",o,n)})),e.post("/:projectId/run",L(async t=>{let{services:r,outgoing:o}=t.env,n=t.env.project;await O(n,r),await ce(r).start(n.id,o)})),e.post("/:projectId/run/stop",async t=>{let r=t.env.project;return w(t,await ce(t.env.services).stop(r.id))}),e.get("/:projectId/run/status",async t=>{let r=t.env.project;return w(t,await ce(t.env.services).status(r.id))}),e.get("/:projectId/run/records",async t=>{let r=t.env.project,o=Number(t.req.query("limit")),n=Number.isInteger(o)&&o>0?o:void 0;return w(t,await ce(t.env.services).listRecords(r.id,{limit:n}))})}s(lr,"registerStageRoutes");async function jn(e,t,r,o){let n=r&&/^[A-Za-z0-9_-]+$/.test(r)?r:null;if(n&&t){let c=await t.resolve(e.id,n,o);return V.join(e.dir,c.templatePath)}let i=await D(e);if(n){let c=`workflows/${n}/workflow.yml`;if(i.some(u=>u.path===c))return V.join(e.dir,c)}if(i.some(c=>c.path==="workflow.yml"))return V.join(e.dir,"workflow.yml");let a=i.find(c=>/^workflows\/[^/]+\/workflow\.ya?ml$/.test(c.path));return V.join(e.dir,a?.path??"workflow.yml")}s(jn,"resolvePreviewWorkflowPath");function mr(){let e=new kn;return e.get("/",async t=>w(t,{projects:await t.env.services.projectStore.projects.list(t.env.scope)})),e.post("/",async t=>{let{services:r,scope:o,incoming:n}=t.env,i=await I(n),a=await r.projectStore.projects.create(o,{title:i.title,files:i.files});await O(a,r);let c={project:a};return a.files&&(c.files=a.files),a.warnings&&a.warnings.length>0&&(c.warnings=a.warnings),w(t,c)}),e.use("/:projectId",We),e.use("/:projectId/*",We),e.delete("/:projectId",async t=>{let{services:r,scope:o}=t.env,n=t.env.project;return r.runtime.runSessions.has(n.id)?F(t,409,"project_busy","\u9879\u76EE\u5B58\u5728\u6D3B\u8DC3\u7684\u8FD0\u884C\u4F1A\u8BDD\uFF0C\u65E0\u6CD5\u5220\u9664"):(await r.projectStore.projects.delete(n,o),await he(n),await it(r,n.id),w(t,{ok:!0,id:n.id}))}),e.patch("/:projectId",async t=>{let{services:r,scope:o,incoming:n}=t.env,i=await I(n);return w(t,{project:await r.projectStore.projects.updateMeta(t.env.project,{title:i.title},o)})}),e.post("/:projectId/init",L(async t=>{let{services:r,incoming:o,outgoing:n}=t.env,i=t.env.project;await O(i,r);let a=K(t);await Tt(i.id,r.workspace,r.projectStore.instances,o,n,(c,u)=>a.bindProjectMachine(c,u)),await fe(r,i,"init")})),e.get("/:projectId/engine",async t=>{let r=t.env.project,o=K(t),n=await o.getProjectMachine(r.id);if(!n)return w(t,{machineId:null,machine:null,status:"unbound"});let i=o.isMachineOnline(n),c=(o.listMachines()??[]).find(u=>u.machineId===n);return w(t,{machineId:n,machine:c??null,status:i?"online":"offline"})}),e.put("/:projectId/engine",async t=>{let{incoming:r}=t.env,o=await I(r);return o.machineId?(await K(t).bindProjectMachine(t.env.project.id,o.machineId),w(t,{ok:!0,machineId:o.machineId})):F(t,422,"INVALID_INPUT","machineId \u4E0D\u80FD\u4E3A\u7A7A")}),e.get("/:projectId/files",async t=>{let r=t.env.project;return w(t,{project:r,files:await D(r)})}),e.put("/:projectId/files",async t=>{let{services:r,scope:o,incoming:n}=t.env,i=t.env.project,a=await I(n);if(!Array.isArray(a.files))return F(t,422,"INVALID_INPUT","files \u4E0D\u80FD\u4E3A\u7A7A");let c=await r.projectStore.projects.replaceFiles(i,a.files,o),u=await O(i,r);return w(t,{project:u.project,files:c})}),e.patch("/:projectId/files",async t=>{let{services:r,scope:o,incoming:n}=t.env,i=t.env.project,a=await I(n),c=await r.projectStore.projects.updateFiles(i,{upsert:a.upsert,remove:a.remove},o),u=await O(i,r);return w(t,{project:u.project,files:c})}),e.get("/:projectId/export",async t=>{let{services:r,scope:o}=t.env,n=t.env.project,i=await r.projectStore.projects.exportProject(n,o),a=$t(i),c=r.projectStore.workflows;if(c){let u=await Promise.all((await c.list(n.id,o)).map(async p=>{let l=await c.exportWorkflow(n.id,p.id,o);return{workflow:l.workflow,files:l.files}}));return w(t,{...a,workflows:u})}return w(t,a)}),e.post("/:projectId/validate",async t=>{let r=t.env.project,o=await vn({workDir:r.dir,configPath:V.join(r.dir,"aitable-workflow.config.yml"),workflows:[],workflowPath:V.join(r.dir,"workflow.yml"),recipeDir:V.join(r.dir,"recipes")});return await Ot(r,o),w(t,o)}),e.get("/:projectId/workflow",async t=>{let r=t.env.project,o=await Pn(r.dir),n=await jn(r,t.env.services.projectStore.workflows,t.req.query("workflowId")??null,t.env.scope),i=await fr(n,{envSource:o}).catch(()=>fr(n)),a=V.join(V.dirname(n),"recipes"),c=i.steps.map(u=>({...u,requires_agent:u.recipe?bt(a,u.recipe):!!u.prompt}));return w(t,{workflow:{...i,steps:c}})}),e.post("/:projectId/reset-workflow",async t=>{let r=await mt(t.env.project);return w(t,{project:r,files:await D(r)})}),e.get("/:projectId/state",async t=>{let{services:r}=t.env,o=t.env.project,n=await r.projectStore.instances.getRecoverableState(o);return w(t,Jt(n,r.runtime.runSessions.get(o.id)))}),e.patch("/:projectId/table-id",L(async t=>{let{services:r,incoming:o,outgoing:n}=t.env;await St(t.env.project.id,r.workspace,r.projectStore.instances,o,n)})),Qt(e),lr(e),e}s(mr,"projectRoutes");function gr(){let e=new Tn;return e.onError((t,r)=>F(r,se(t),C(t),T(t),M(t))),e.notFound(t=>F(t,404,"NOT_FOUND","\u63A5\u53E3\u4E0D\u5B58\u5728")),e.use("*",async(t,r)=>{t.env.scope=await t.env.services.identityService.resolveScope(t.env.incoming);let o=t.req.path,n=t.req.method;if(!(tt(o,n)||n==="GET"&&o==="/api/public-config")&&!await t.env.services.identityService.authenticate(t.env.incoming))return rt(t);await r()}),e.get("/api/health",t=>w(t,{ok:!0})),e.get("/check.node",t=>t.text("success")),e.get("/api/public-config",t=>{let r=process.env.RELAY_PUBLIC_GATEWAY_URL?.trim();return w(t,{authEnabled:t.env.services.identityService.authRequired,...r?{relayGatewayUrl:r}:{}})}),e.route("/api/projects",mr()),e.get("/api/agents",t=>w(t,Sn())),e}s(gr,"createHonoApp");import{createApp as qn,logger as Un}from"aitable-workflow-core";var Oe=class{static{s(this,"LocalProjectRepository")}workspace;constructor(t){this.workspace=t}async create(t,r){let o=await ft(this.workspace),n=[];if(r?.files&&r.files.length>0){let i=await je(o,r.files);n.push(...i.warnings);let a=await x(o);return r.title&&r.title.trim()&&(a=await ae(a,{title:r.title.trim()})),{...a,files:i.written,warnings:n}}if(r?.title&&r.title.trim()){let i=await ae(o,{title:r.title.trim()});return n.length?{...i,warnings:n}:{...i}}return n.length?{...o,warnings:n}:{...o}}async list(t){return Le(this.workspace)}async resolve(t,r){return R(this.workspace,t)}async updateMeta(t,r,o){return ae(t,r)}async delete(t,r){await he(t)}async updateFiles(t,r,o){return r.remove&&r.remove.length>0&&await Mt(t,r.remove),r.upsert&&r.upsert.length>0&&await je(t,r.upsert),await x(t),D(t)}async replaceFiles(t,r,o){return await Lt(t,r),await x(t),D(t)}async exportProject(t,r){return Bt(t)}async syncHotFields(){}};import{randomUUID as Dn}from"crypto";import{mkdir as wr,readFile as yr,rm as On,writeFile as hr}from"fs/promises";import te from"path";import{applyRunExecutionEvent as Rn}from"aitable-workflow-core";var Re=new Map,An=/^[a-zA-Z0-9_-]+$/,Ae=class{static{s(this,"LocalWorkflowInstanceStore")}workspace;constructor(t){this.workspace=t}getState(t){return re(t)}getRecoverableState(t){return Wn(t)}createTask(t,r){return xn(t,r)}updateTask(t,r,o){return En(t,r,o)}clearActiveTask(t,r){return Cn(t,r)}appendRunSnapshotEvent(t,r){return _n(t,r)}updateRunSnapshotRecord(t,r,o,n){return Fn(t,r,o,n)}deleteProjectState(t){return bn(this.workspace,t)}};async function bn(e,t){Ve(t);let r=In(e,t);await On(te.join(r,"runtime"),{recursive:!0,force:!0})}s(bn,"deleteProjectState");function In(e,t){return Ve(t),te.join(e.projectsRoot,t)}s(In,"projectDir");async function xn(e,t){return ue(e,async()=>{await Ie(e);let r=await re(e),o=(r.stages[t]?.task.attempt??0)+1,n=new Date().toISOString(),i={id:`task_${Dn().slice(0,8)}`,projectId:e.id,stage:t,status:"running",attempt:o,startedAt:n,updatedAt:n,currentAction:"\u4EFB\u52A1\u5DF2\u542F\u52A8"};return await be(e,i),await Q(e,Te(r,i)),i})}s(xn,"createTask");async function En(e,t,r){return ue(e,async()=>{let o=await Mn(e,t),n=new Date().toISOString(),i={...o,...r,id:o.id,projectId:o.projectId,stage:o.stage,attempt:o.attempt,startedAt:o.startedAt,updatedAt:n,completedAt:r.completedAt??(Se(r.status)?n:o.completedAt),block:Object.prototype.hasOwnProperty.call(r,"block")?r.block:Se(r.status)?void 0:o.block};await be(e,i);let a=await re(e);return await Q(e,Te(a,i)),i})}s(En,"updateTask");async function re(e){await Ie(e);let t=await yr(vr(e),"utf8").catch(()=>"");if(t.trim()){let r=Ln(t),o=U(e,r);return r.repaired&&await Q(e,o),o}return U(e,{projectId:e.id,activeStage:null,stages:{},updatedAt:e.updatedAt})}s(re,"getState");async function _n(e,t){return ue(e,async()=>{let r=await re(e),o=Rn(r.runSnapshot??{records:[],logs:[]},t),n=U(e,{...r,runSnapshot:o,updatedAt:new Date().toISOString()});return await Q(e,n),n})}s(_n,"appendRunSnapshotEvent");async function Fn(e,t,r,o){return ue(e,async()=>{let n=await re(e);if(!n.runSnapshot)return n;let i=Date.now(),a={records:$n(n.runSnapshot.records,t,u=>({...u,...r,updatedAt:i})),logs:o?Hn(n.runSnapshot.logs,t,r.currentStepId??"run","info",o):n.runSnapshot.logs},c=U(e,{...n,runSnapshot:a,updatedAt:new Date().toISOString()});return await Q(e,c),c})}s(Fn,"updateRunSnapshotRecord");async function Wn(e){return ue(e,async()=>{let t=await re(e),r=t.stages.design?.task,o=await gt(e);if(o.status==="ready"){if(!r||r.status==="succeeded")return t;let c=new Date().toISOString(),u={...r,status:"succeeded",updatedAt:c,completedAt:r.completedAt??c,currentAction:"\u5DE5\u4F5C\u6D41\u8BBE\u8BA1\u5B8C\u6210",checkpoint:"done",block:void 0,error:void 0};await be(e,u);let p=U(e,{...t,activeStage:t.activeStage==="design"?null:t.activeStage,stages:{...t.stages,design:we(u)},updatedAt:c});return await Q(e,p),p}if(r?.status!=="succeeded")return t;let n=new Date().toISOString(),i={...r,status:"failed",updatedAt:n,completedAt:n,currentAction:o.status==="artifact_missing"?"\u8BBE\u8BA1\u4EA7\u7269\u7F3A\u5931\uFF0C\u7B49\u5F85\u91CD\u65B0\u751F\u6210":"\u8BBE\u8BA1\u4EA7\u7269\u72B6\u6001\u5F02\u5E38\uFF0C\u7B49\u5F85\u91CD\u65B0\u751F\u6210",checkpoint:o.status,error:{code:o.status==="artifact_missing"?"ARTIFACT_MISSING":"DESIGN_ARTIFACT_INVALID",message:o.errors.join("\uFF1B")||"\u8BBE\u8BA1\u4EA7\u7269\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u5DE5\u4F5C\u6D41\u8BBE\u8BA1\u3002",retryable:!0,details:{missing:o.missing}}},a=U(e,{...t,activeStage:t.activeStage==="design"?null:t.activeStage,stages:{...t.stages,design:we(i)},updatedAt:n});return await be(e,i),await Q(e,a),a})}s(Wn,"getRecoverableState");async function Cn(e,t){return ue(e,async()=>{let r=await re(e);if(N(r)?.id!==t)return r;let o=U(e,{...r,activeStage:null,updatedAt:new Date().toISOString()});return await Q(e,o),o})}s(Cn,"clearActiveTask");function Ge(e){return te.join(e.dir,"runtime")}s(Ge,"runtimeDir");function Nn(e){return te.join(Ge(e),"artifacts")}s(Nn,"artifactsDir");function kr(e){return te.join(Ge(e),"tasks")}s(kr,"tasksDir");function vr(e){return te.join(Ge(e),"state.json")}s(vr,"statePath");function Pr(e,t){return Ve(t),te.join(kr(e),`${t}.json`)}s(Pr,"taskPath");async function Ie(e){await wr(kr(e),{recursive:!0}),await wr(Nn(e),{recursive:!0})}s(Ie,"ensureRuntimeDirs");async function Mn(e,t){let r=await yr(Pr(e,t),"utf8");return JSON.parse(r)}s(Mn,"readTask");async function be(e,t){await Ie(e),await hr(Pr(e,t.id),`${JSON.stringify(t,null,2)}
18
- `,"utf8")}s(be,"writeTask");async function Q(e,t){await Ie(e),await hr(vr(e),`${JSON.stringify(t,null,2)}
19
- `,"utf8")}s(Q,"writeProjectState");async function ue(e,t){let r=e.dir||e.id,n=(Re.get(r)??Promise.resolve()).catch(()=>{}).then(t),i=n.then(()=>{},()=>{});Re.set(r,i);try{return await n}finally{Re.get(r)===i&&Re.delete(r)}}s(ue,"withProjectStateWrite");function Ln(e){try{return JSON.parse(e)}catch(t){let r=Bn(e);if(!r)throw t;return{...JSON.parse(r),repaired:!0}}}s(Ln,"parseProjectStateJson");function Bn(e){let t=e.indexOf("{");if(t<0)return null;let r=0,o=!1,n=!1;for(let i=t;i<e.length;i+=1){let a=e[i];if(o){n?n=!1:a==="\\"?n=!0:a==='"'&&(o=!1);continue}if(a==='"')o=!0;else if(a==="{")r+=1;else if(a==="}"&&(r-=1,r===0))return e.slice(t,i+1)}return null}s(Bn,"extractFirstJsonObject");function $n(e,t,r){return e.map(o=>o.id===t?r(o):o)}s($n,"updateRunRecord");function Hn(e,t,r,o,n){return[...e,{id:`run_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,7)}`,ts:Date.now(),itemId:t,stepId:r,level:o,text:n}].slice(-300)}s(Hn,"appendRunLog");function Ve(e){if(!An.test(e))throw Object.assign(new Error(`\u975E\u6CD5\u9879\u76EE ID: ${e}`),{code:"INVALID_PROJECT_ID"})}s(Ve,"assertProjectId");var xe=class{static{s(this,"LocalProjectStore")}projects;instances;workflows;constructor(t){this.projects=new Oe(t),this.instances=new Ae(t)}async prepareWorkdir(t,r){return{project:await x(t),materialized:0,warnings:[]}}async commitWorkdir(t,r){}async cascadeDelete(t){}};var Ee=class{static{s(this,"LocalExecutionBackend")}kind="local";ensureAvailable(){}async buildDesignExecutor(){}buildSetupExecutor(){}buildDwsRunner(){}listMachines(){}isMachineOnline(){return!1}async getProjectMachine(){return null}async bindProjectMachine(){throw Object.assign(new Error("\u5F15\u64CE\u7BA1\u7406\u6682\u4E0D\u53EF\u7528"),{code:"NOT_AVAILABLE",status:503})}async unbindProjectMachine(){}};var _e=class{static{s(this,"LocalIdentityService")}authRequired=!1;authenticate(t){return Xe(t)}async resolveScope(t){let r=t.headers["x-actor-id"],o=Array.isArray(r)?r[0]:r;return o?.trim()?{actorId:o.trim()}:{}}};function jr(e={}){let t=e.workspace??lt({dataRoot:e.dataRoot});return{workspace:t,runtime:Ut(e.appFactory??((r,o)=>qn(r,{onRunEvent:o}))),projectStore:e.projectStore??new xe(t),execution:e.execution??new Ee,identityService:e.identityService??new _e,extendApp:e.extendApp,webRoot:e.webRoot}}s(jr,"createServerServices");function Tr(e=process.env){let t=e.AITABLE_RELAY_BOOTSTRAP_KEY;return typeof t=="string"&&t.trim()!==""?(Un.warn("server","AITABLE_RELAY_BOOTSTRAP_KEY \u5DF2\u5E9F\u5F03\u5E76\u88AB\u5FFD\u7565\uFF1Abootstrap \u8BA4\u8BC1\u5DF2\u79FB\u9664\uFF0C\u8BF7\u6539\u7528 owner-token\uFF08sk_owner_*\uFF09\u6216 per-machine key\uFF08sk_machine_*\uFF09"),!0):!1}s(Tr,"warnDeprecatedBootstrapKey");var zn="127.0.0.1",Xa=8787;function Fe(e){if(typeof e!="string")return;let t=e.trim();return t||void 0}s(Fe,"readEnvString");function Jn(e){let t=Fe(e);if(t===void 0)return;let r=Number.parseInt(t,10);return Number.isFinite(r)&&r>0?r:void 0}s(Jn,"readEnvPort");function Sr(e={},t=process.env){let r=e.host??Fe(t.AITABLE_WORKFLOW_HOST)??zn,o=e.port??Jn(t.AITABLE_WORKFLOW_PORT)??8787,n=e.dataRoot??Fe(t.AITABLE_WORKFLOW_DATA_DIR)??void 0,i=e.webRoot??Fe(t.AITABLE_WORKFLOW_WEB_ROOT)??void 0;return{host:r,port:o,dataRoot:n,webRoot:i}}s(Sr,"resolveServeConfig");import Ye from"path";import{readFile as Kn,access as Gn}from"fs/promises";import{resolveInsideRoot as Vn}from"aitable-workflow-core";var Yn={".html":"text/html; charset=utf-8",".js":"text/javascript; charset=utf-8",".mjs":"text/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".json":"application/json; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".ico":"image/x-icon",".webp":"image/webp",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".map":"application/json; charset=utf-8",".txt":"text/plain; charset=utf-8"};async function Dr(e,t,r,o){let n=Ye.resolve(e),i=Ye.join(n,"index.html"),a=decodeURIComponent(t).replace(/^\/+/,""),c;try{c=a===""?i:Vn(n,a,{allowRoot:!1})}catch{c=i}try{await Gn(c)}catch{c=i}try{let u=await Kn(c),p=Ye.extname(c).toLowerCase(),l=Yn[p]??"application/octet-stream";if(o.writeHead(200,{"Content-Type":l,"Content-Length":u.byteLength}),r.method==="HEAD"){o.end();return}o.end(u)}catch{q(o,404,"WEB_NOT_BUILT","\u672C\u5730 web \u8D44\u6E90\u4E0D\u5B58\u5728\uFF08\u8BF7\u5148\u6784\u5EFA\u524D\u7AEF\u6216\u68C0\u67E5 --web-root\uFF09")}}s(Dr,"serveStaticWeb");async function mc(e={}){Tr();let t=Sr({port:e.port,host:e.host,dataRoot:e.dataRoot,webRoot:e.webRoot}),r=e.services??jr({dataRoot:t.dataRoot,appFactory:e.appFactory,webRoot:t.webRoot}),o=Qn.createServer((n,i)=>{Xn(r,n,i)});return await new Promise(n=>o.listen(t.port,t.host,n)),console.log(`aitable-workflow server listening on http://${t.host}:${t.port}`),console.log(`data root: ${r.workspace.dataRoot}`),o}s(mc,"startServer");var Or=new WeakMap;function Zn(e){let t=Or.get(e);return t||(t=gr(),e.extendApp?.(t),Or.set(e,t)),t}s(Zn,"appFor");async function Xn(e,t,r){if(Ze(t,r),t.method==="OPTIONS"){r.writeHead(204).end();return}let o=new URL(t.url??"/","http://localhost");if(e.webRoot&&(t.method==="GET"||t.method==="HEAD")&&!o.pathname.startsWith("/api/")&&o.pathname!=="/check.node"){await Dr(e.webRoot,o.pathname,t,r);return}let n={incoming:t,outgoing:r,services:e,scope:{}},i=new Request(o,{method:t.method??"GET",headers:es(t.headers)}),a=await Zn(e).fetch(i,n);if(n.rawHandled)return;let c={};a.headers.forEach((p,l)=>{c[l]=p});let u=a.body?Buffer.from(await a.arrayBuffer()):void 0;r.writeHead(a.status,c),r.end(u)}s(Xn,"handleRequest");function es(e){let t=new Headers;for(let[r,o]of Object.entries(e))if(o!==void 0)if(Array.isArray(o))for(let n of o)t.append(r,n);else t.set(r,o);return t}s(es,"toFetchHeaders");import Rr from"path";function hc(e){let t=Rr.normalize(e).split(Rr.sep).join("/");return t==="workflow.yml"?"workflow":t==="views.yml"?"views":t.startsWith("recipes/")?"recipe":"other"}s(hc,"workflowFileKind");var Ar=class{static{s(this,"LocalProjectMutexStore")}queues=new Map;withProjectLock(t,r){let n=(this.queues.get(t)??Promise.resolve()).then(r,r);return this.queues.set(t,n),n.then(()=>{this.queues.get(t)===n&&this.queues.delete(t)},()=>{this.queues.get(t)===n&&this.queues.delete(t)}).catch(()=>{}),n}};export{zn as DEFAULT_SERVE_HOST,Xa as DEFAULT_SERVE_PORT,ze as LeaseRunController,Ee as LocalExecutionBackend,_e as LocalIdentityService,Ar as LocalProjectMutexStore,Oe as LocalProjectRepository,xe as LocalProjectStore,Ue as LocalRunController,Ae as LocalWorkflowInstanceStore,N as activeTaskOf,ne as apiError,Ft as assertSafeProjectPath,_t as assertSafeRelativePath,qs as assertSafeWorkflowPath,mo as assertWritableKind,K as backendOf,$e as buildAITableUrl,it as cascadeDeleteProject,me as checksumContent,fe as commitStageOnTerminal,B as configPath,gr as createHonoApp,ft as createProject,Ut as createServerRuntime,jr as createServerServices,lt as createWorkspace,he as deleteProject,F as errorBody,C as errorCode,M as errorDetails,T as errorMessage,se as errorStatus,Xn as handleRequest,zt as handleRunRuntime,Wt as isForbiddenWritable,Mr as isStageActiveFor,Se as isTerminalStatus,w as jsonBody,Ct as kindOf,D as listProjectFiles,Le as listProjects,Te as mergeTaskIntoState,U as normalizeProjectState,Jt as overlayRunSessionState,O as prepareWorkdirGuarded,L as raw,Ur as readDesignManifest,I as readJson,Mt as removeProjectFiles,Lt as replaceProjectFiles,mt as resetProjectWorkflow,R as resolveProject,We as resolveProjectMw,ce as resolveRunController,Sr as resolveServeConfig,q as sendError,_ as sendJson,rs as sendUnauthorized,we as stageStateOf,mc as startServer,x as touchProject,rt as unauthorized,ae as updateProjectMeta,go as validateConfigScene,gt as validateDesignArtifacts,Tr as warnDeprecatedBootstrapKey,hc as workflowFileKind,Be as writeDesignManifest,je as writeProjectFiles};
15
+ `)}o(se,"writeSse");var at=class{constructor(t){this.services=t}services;static{o(this,"LocalRunController")}mode="local";async start(t,r){Q(r),await pr(t,this.services.workspace,this.services.projectStore.instances,this.services.runtime,r,se)}async stop(t){let r=this.services.runtime.runSessions.get(t);if(!r)return{mode:this.mode,running:!1,stopped:!1};if(!r.app)throw Object.assign(new Error("\u8FD0\u884C\u4F1A\u8BDD\u6B63\u5728\u542F\u52A8\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"),{code:"RUN_STARTING",status:409});return await r.app.stop(),{mode:this.mode,running:!1,stopped:!0,taskId:r.taskId}}async status(t){let r=this.services.runtime.runSessions.get(t);return{mode:this.mode,running:r!=null,taskId:r?.taskId??null}}async listRecords(t,r){let s=await E(this.services.workspace,t),i=(await this.services.projectStore.instances.getState(s)).runSnapshot?.records??[],a=r?.limit??100;return{items:[...i].sort((u,p)=>p.updatedAt-u.updatedAt).slice(0,a).map(u=>({itemId:u.id,title:u.title,state:u.state,currentStepId:u.currentStepId,doneStepIds:u.doneStepIds,completedAt:u.status==="done"?new Date(u.updatedAt).toISOString():null,updatedAt:new Date(u.updatedAt).toISOString(),status:u.status})),counts:{total:i.length,completed:i.filter(u=>u.status==="done").length}}}},ct=class{constructor(t,r){this.scheduler=t;this.records=r}scheduler;records;static{o(this,"LeaseRunController")}mode="lease";async start(t,r){m(r,{mode:this.mode,action:"start",schedule:await this.scheduler.start(t)})}async stop(t){return{mode:this.mode,action:"stop",schedule:await this.scheduler.stop(t)}}async status(t){return{mode:this.mode,action:"status",schedule:await this.scheduler.getStatus(t)}}async listRecords(t,r){return this.records?this.records.listRecords(t,r):{items:[],counts:{total:0,completed:0}}}};function ye(e){let{runScheduler:t,runRecords:r}=e.execution;return t?new ct(t,r):new at(e)}o(ye,"resolveRunController");import Io from"path";import{collectDesignContext as Rr,runDesign as Ao,runClarifyRound as Eo,syncFieldsFromWorkflow as _o,syncViewsFromDesign as Fo,summarizeForms as Wo,validateFormAnswers as No,pruneHiddenAnswers as Lo,logger as xr}from"aitable-workflow-core";var Me=class{static{o(this,"DesignEventBuffer")}capacity;events=[];seq=0;constructor(t=100){this.capacity=t>0?t:100}push(t){this.seq+=1;let r={seq:this.seq,event:t};return this.events.push(r),this.events.length>this.capacity&&this.events.shift(),r}get cursor(){return this.seq}get size(){return this.events.length}replayAfter(t){return t===void 0||Number.isNaN(t)?[...this.events]:this.events.filter(r=>r.seq>t)}};async function br(e,t,r,s,n){let i=await E(t,e),a=await r.getState(i),c=a.stages.design?.task,u=c?.status==="failed"?c:null;if(u){await r.clearActiveTask(i,u.id),m(s,{task:u,error:{code:"DESIGN_RETRY_REQUIRED",message:u.error?.message??"\u4E0A\u4E00\u6B21 Design \u5DF2\u5931\u8D25\uFF0C\u8BF7\u70B9\u51FB\u91CD\u65B0\u751F\u6210\u5DE5\u4F5C\u6D41\u8BBE\u8BA1\u3002",retryable:!0}},409);return}let p=q(a),f=p?.stage==="design"?p:null,g=f||await r.createTask(i,"design");try{let v=J(i),P=await Rr({configPath:v,yes:!0}),d=P.agentType?await Eo(P,{...await Ir(n,i,P.agentType),timeoutMs:P.agentTimeoutMs,interactive:!0,judgeOnly:!0}):void 0;if(!d?.form){let _=P.agentType?"Agent \u5224\u65AD\u5F53\u524D\u9700\u6C42\u5DF2\u8DB3\u591F\u660E\u786E\uFF0C\u672A\u63D0\u51FA\u6F84\u6E05\u95EE\u9898\uFF0C\u76F4\u63A5\u5F00\u59CB\u751F\u6210\u3002":"\u5F53\u524D\u5B9E\u4F8B\u672A\u914D\u7F6E coding agent\uFF0C\u8DF3\u8FC7\u9700\u6C42\u6F84\u6E05\u3002",ae={form:{id:"clarify",title:"\u65E0\u9700\u6F84\u6E05",questions:[]},answers:{},summary:_,clarifications:_,confirmed:!0,...d?.sessionId?{agentSessionId:d.sessionId}:{}},ce=await r.updateTask(i,g.id,{status:"running",checkpoint:"discovery_confirmed",currentAction:"\u9700\u6C42\u5DF2\u660E\u786E\uFF0C\u51C6\u5907\u751F\u6210\u5DE5\u4F5C\u6D41\u65B9\u6848",block:void 0,error:void 0,outputs:{...g.outputs,discovery:ae,clarifications:_}});m(s,{task:ce,discovery:ae});return}let k={form:d.form,answers:Mo(d.form),summary:d.form.description??d.form.title,confirmed:!1,...d.sessionId?{agentSessionId:d.sessionId}:{}},y=await r.updateTask(i,g.id,{status:"blocked",checkpoint:"discovery_waiting_user",currentAction:"\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u9700\u6C42\u6F84\u6E05",block:{reason:"discovery_required",message:"\u8BF7\u5148\u786E\u8BA4 Design \u9636\u6BB5\u7684\u9700\u6C42\u6F84\u6E05\u5361\u7247\uFF0C\u518D\u5F00\u59CB\u751F\u6210\u5DE5\u4F5C\u6D41\u65B9\u6848\u3002",actionLabel:"\u786E\u8BA4\u540E\u5F00\u59CB\u751F\u6210"},outputs:{...g.outputs,discovery:k}});m(s,{task:y,discovery:k})}catch(v){let P=await r.updateTask(i,g.id,{status:"failed",checkpoint:"discovery_precheck",currentAction:"\u9700\u6C42\u6F84\u6E05\u51C6\u5907\u5931\u8D25",error:{code:"DESIGN_PRECHECK_FAILED",message:x(v),retryable:!0,details:U(v)}});m(s,{task:P,error:me(v)},500)}}o(br,"handleDesignDiscovery");async function Ir(e,t,r){let s=await e.buildDesignExecutor({projectId:t.id,agent:r,projectDir:t.dir});return s?{executor:s}:{}}o(Ir,"relayExecutorFor");function Mo(e){let t=Object.fromEntries(e.questions.map(r=>[r.id,r.defaultValue??(r.type==="checkbox"?[]:"")]));return Lo(e,t)}o(Mo,"seedAnswers");async function Ar(e,t,r,s,n){let i=await E(t,e),a=await r.getState(i),c=q(a),u=c?.stage==="design"?c:a.stages.design?.task,p=u?.outputs?.discovery;if(!u||!p){S(n,409,"DISCOVERY_NOT_READY","\u8BF7\u5148\u751F\u6210\u9700\u6C42\u6F84\u6E05\u5361\u7247\uFF0C\u518D\u786E\u8BA4 Design \u8F93\u5165\u3002");return}let f=s.answers,g=f&&Object.keys(f).length>0?f:p.answers,v=No(p.form,g);if(v.length>0){S(n,400,"DISCOVERY_INVALID_ANSWERS",`\u9700\u6C42\u6F84\u6E05\u7B54\u6848\u4E0D\u5B8C\u6574\uFF1A${v.map(y=>y.message).join("\uFF1B")}`,{issues:v});return}let P=Wo([{form:p.form,answers:g}]),d={...p,answers:g,clarifications:P,summary:P.replace(/\s+/g," ").trim(),confirmed:!0},k=await r.updateTask(i,u.id,{status:"running",checkpoint:"discovery_confirmed",currentAction:"\u9700\u6C42\u6F84\u6E05\u5DF2\u786E\u8BA4\uFF0C\u51C6\u5907\u751F\u6210\u5DE5\u4F5C\u6D41\u65B9\u6848",block:void 0,error:void 0,outputs:{...u.outputs,discovery:d,clarifications:P}});m(n,{task:k,discovery:d})}o(Ar,"handleDesignDiscoveryConfirm");async function Er(e,t,r,s,n,i){let a=await E(t,e);try{n.ensureAvailable()}catch(h){S(s,pe(h),B(h),x(h));return}let c=await r.getState(a),u=q(c),p=u?.stage==="design"?u:null;if(p&&p.status==="blocked"){Q(s),se(s,"task.blocked",{type:"task.blocked",task:p}),s.end();return}if(ge.has(a.id)){Q(s);let h=Te.get(a.id);if(h){for(let D of h.buffer.replayAfter(i))Ze(s,D.seq,D.event);h.subscribers.add(s),s.on("close",()=>h.subscribers.delete(s));return}p&&se(s,"task.snapshot",{type:"task.snapshot",task:p}),s.end();return}let f=c.stages.design?.task,g=f?.outputs?.discovery,v=g?.confirmed&&typeof f?.outputs?.clarifications=="string";if(p&&p.status==="running"&&!v){Q(s),se(s,"task.snapshot",{type:"task.snapshot",task:p}),s.end();return}let P=f?.status==="failed"&&g?.confirmed&&typeof f.outputs?.clarifications=="string",d=p&&v?p:P?await r.updateTask(a,(await r.createTask(a,"design")).id,{checkpoint:"discovery_confirmed",currentAction:"\u91CD\u65B0\u751F\u6210\u5DE5\u4F5C\u6D41\u8BBE\u8BA1",outputs:{discovery:g,clarifications:f.outputs?.clarifications}}):await r.createTask(a,"design");ge.add(a.id),Q(s);let k={buffer:new Me,subscribers:new Set([s])};Te.set(a.id,k),s.on("close",()=>k.subscribers.delete(s));let y=o(h=>Tt(k,h),"send"),_=Date.now(),ae=_,ce=0,ue=[],Z=[],H=o((h,D,j)=>{let $=Date.now(),fe=$-_,M=$-ae;ae=$,ue.push({phase:h,message:D,elapsedMs:fe,deltaMs:M,detail:j}),xr.info("design-runtime",D,{projectId:a.id,taskId:d.id,phase:h,elapsedMs:fe,deltaMs:M,...j?{detail:j}:{}}),y({type:"design.progress",phase:h,message:D,elapsedMs:fe,deltaMs:M,detail:j})},"emitProgress"),F=o(async(h,D,j)=>{H(h,D,j);let $=await r.updateTask(a,d.id,{currentAction:D,checkpoint:h});y({type:"task.progress",taskId:$.id,message:D,checkpoint:h})},"progress"),Oe="",Pe=!1,ke=!1,oe=o(async()=>{if(Pe){ke=!0;return}Pe=!0;try{do{ke=!1;let D=(await R(a)).filter(j=>j.path!=="project.json").map(j=>`${j.path}:${j.content}`).join("|");if(D&&D!==Oe){Oe=D;let j=await L(a),$=await R(j);H("file_snapshot",`\u540C\u6B65 ${$.length} \u4E2A\u751F\u6210\u6587\u4EF6`,{fileCount:$.length}),y({type:"design.files",files:$})}}while(ke)}finally{Pe=!1}},"sendFileSnapshot");try{y({type:"task.started",task:d}),y({type:"design.started",projectId:e}),await oe();let h,D;try{await F("precheck","\u8BFB\u53D6 Design \u914D\u7F6E"),D=J(a),h=await Rr({configPath:D,yes:!0}),await F("precheck_done","Design \u914D\u7F6E\u8BFB\u53D6\u5B8C\u6210",{agent:h.agentType,timeoutMs:h.agentTimeoutMs})}catch(w){let z=await r.updateTask(a,d.id,{status:"failed",checkpoint:"precheck",currentAction:"Design \u9884\u68C0\u67E5\u5931\u8D25",error:{code:"DESIGN_PRECHECK_FAILED",message:x(w),retryable:!0,details:U(w)}});y({type:"task.failed",task:z}),y({type:"error",error:me(w)}),s.end();return}let j=d.outputs?.discovery,$=typeof d.outputs?.clarifications=="string"?d.outputs.clarifications:j?.clarifications;if(!j?.confirmed||!$){await F("waiting_input","\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u9700\u6C42\u6F84\u6E05");let w=await r.updateTask(a,d.id,{status:"blocked",checkpoint:"discovery_waiting_user",currentAction:"\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u9700\u6C42\u6F84\u6E05",block:{reason:"discovery_required",message:"\u8BF7\u5148\u786E\u8BA4 Design \u9636\u6BB5\u7684\u9700\u6C42\u6F84\u6E05\u5361\u7247\uFF0C\u518D\u5F00\u59CB\u751F\u6210\u5DE5\u4F5C\u6D41\u65B9\u6848\u3002",actionLabel:"\u786E\u8BA4\u540E\u5F00\u59CB\u751F\u6210"},outputs:d.outputs});y({type:"task.blocked",task:w}),s.end();return}h.clarifications=$,await F("brief_ready","\u9700\u6C42\u6458\u8981\u5DF2\u786E\u8BA4\uFF0C\u51C6\u5907\u542F\u52A8 Agent"),y({type:"design.discovery",summary:j.summary}),await F("agent_start","\u542F\u52A8 Coding Agent \u751F\u6210\u6D41\u7A0B\u65B9\u6848",{agent:h.agentType,timeoutMs:null,timeoutSource:"disabled"});let fe=!1,M=await Ao(h,{...await Ir(n,a,h.agentType),...j.agentSessionId?{resume:{sessionId:j.agentSessionId,answered:[{form:j.form,answers:j.answers}]}}:{},timeoutMs:null,onRoundStart:o((w,z)=>{H("agent_round",`\u7B2C ${w} \u8F6E ${z?"\u4FEE\u6B63":"\u751F\u6210"}\u5F00\u59CB`,{round:w,isFix:z}),y({type:"design.round_start",round:w,isFix:z})},"onRoundStart"),onEvent:o(w=>{Co(w)&&(Z.push($o(w)),Z.length>30&&Z.shift(),fe||(fe=!0,H("agent_first_event",`\u6536\u5230 Agent \u9996\u4E2A\u4E8B\u4EF6\uFF1A${w.type}`,{eventType:w.type})),w.type==="tool_use"&&H("agent_tool","Agent \u8C03\u7528\u5DE5\u5177",{toolName:w.toolName}),(w.type==="text"||w.type==="thinking")&&Date.now()-ce>5e3&&(ce=Date.now(),H("agent_output",w.type==="thinking"?"Agent \u6B63\u5728\u63A8\u7406":"Agent \u8F93\u51FA\u5185\u5BB9",{eventType:w.type,preview:w.content?.replace(/\s+/g," ").slice(0,120)})),y({type:"design.agent_event",event:w}),(w.type==="tool_use"||w.type==="tool_result")&&oe())},"onEvent"),onAgentLifecycle:o(w=>{H(Bo(w.phase),qo(w),Uo(w))},"onAgentLifecycle"),onValidation:o((w,z)=>{H("validating",w.valid?"\u6D41\u7A0B\u65B9\u6848\u6821\u9A8C\u901A\u8FC7":`\u6D41\u7A0B\u65B9\u6848\u6821\u9A8C\u53D1\u73B0 ${w.errors.length} \u4E2A\u95EE\u9898`,{round:z,valid:w.valid,errorCount:w.errors.length}),y({type:"design.validation",round:z,validation:w}),oe()},"onValidation")});if(!M.success){await oe();let w=await r.updateTask(a,d.id,{status:"failed",checkpoint:"validating",currentAction:"\u771F\u5B9E\u8BBE\u8BA1\u672A\u901A\u8FC7\u6821\u9A8C",error:{code:"DESIGN_VALIDATION_FAILED",message:M.response,retryable:!0},outputs:{...d.outputs,diagnostics:ut(_,ue,Z,M.validation)}});await Or(a,w,M.response),y({type:"task.failed",task:w}),y({type:"design.done",success:!1,rounds:M.rounds,response:M.response}),s.end();return}await F("sync_fields","\u540C\u6B65\u5B57\u6BB5\u8BA1\u5212\u5230\u914D\u7F6E");for(let w of h.workflows)await _o({workflowPath:w.workflowPath,configPath:D,autoUpdate:!0}).catch(z=>(xr.warn("design","\u5B57\u6BB5\u540C\u6B65\u5931\u8D25(\u4E0D\u963B\u65AD)",{workflow:w.id,error:x(z)}),null));await F("sync_views","\u540C\u6B65\u89C6\u56FE\u8BA1\u5212\u5230\u914D\u7F6E"),await Fo({viewsPath:Io.resolve(h.workDir,"views.yml"),configPath:D,autoUpdate:!0}).catch(()=>null),await oe(),await F("manifest","\u5199\u5165 Design \u4EA7\u7269\u6E05\u5355");let Kr=await R(a),ft=Jo(a,d,Kr);await rt(a,ft);let Vr=await r.updateTask(a,d.id,{status:"succeeded",currentAction:"\u5DE5\u4F5C\u6D41\u8BBE\u8BA1\u5B8C\u6210",checkpoint:"done",outputs:{...d.outputs,manifest:ft,diagnostics:ut(_,ue,Z,M.validation)}});y({type:"task.succeeded",task:Vr}),y({type:"design.done",success:!0,rounds:M.rounds,response:M.response}),s.end()}catch(h){await oe().catch(()=>{});let D=await r.updateTask(a,d.id,{status:"failed",checkpoint:"generating",currentAction:"\u771F\u5B9E\u8BBE\u8BA1\u751F\u6210\u5931\u8D25",error:{code:B(h)==="INTERNAL_ERROR"?"DESIGN_GENERATION_FAILED":B(h),message:x(h),retryable:!0,details:U(h)},outputs:{...d.outputs,diagnostics:ut(_,ue,Z)}});await Or(a,D,x(h)),y({type:"task.failed",task:D}),y({type:"error",error:me(h)}),y({type:"design.done",success:!1,rounds:0,response:x(h)}),s.end()}finally{St(a.id)}}o(Er,"handleDesign");function Co(e){return e.type!=="text"&&e.type!=="thinking"?!0:typeof e.content=="string"&&e.content.trim().length>0}o(Co,"isDisplayableAgentEvent");function $o(e){return e.type==="tool_use"?{type:e.type,toolName:e.toolName,toolInput:e.toolInput}:e.type==="tool_result"||e.type==="error"||e.type==="text"||e.type==="thinking"?{type:e.type,content:e.content?.replace(/\s+/g," ").slice(0,240)}:{type:e.type}}o($o,"summarizeAgentEvent");function Bo(e){return`agent_${e}`}o(Bo,"agentLifecyclePhase");function qo(e){switch(e.phase){case"process_spawned":return"Coding Agent \u8FDB\u7A0B\u5DF2\u542F\u52A8";case"first_stdout":return"Coding Agent \u9996\u6B21\u8F93\u51FA stdout";case"first_stderr":return"Coding Agent \u9996\u6B21\u8F93\u51FA stderr";case"process_closed":return"Coding Agent \u8FDB\u7A0B\u5DF2\u9000\u51FA"}}o(qo,"agentLifecycleMessage");function Uo(e){return{provider:e.provider,phase:e.phase,elapsedMs:e.elapsedMs,...e.pid?{pid:e.pid}:{},...e.cmd?{cmd:e.cmd}:{},...e.cwd?{cwd:e.cwd}:{},...e.exitCode!==void 0?{exitCode:e.exitCode}:{}}}o(Uo,"sanitizeAgentLifecycle");function ut(e,t,r,s){return{elapsedMs:Date.now()-e,timeline:t.slice(-40),recentAgentEvents:r,validation:s}}o(ut,"buildDesignDiagnostics");function Jo(e,t,r){let s=r.filter(a=>Ho(a.path)).map(a=>({path:a.path,kind:_r(a.path),checksum:De(a.content),required:zo(a.path)})),n=s.filter(a=>a.required).map(a=>a.path),i=new Date().toISOString();return{projectId:e.id,taskId:t.id,designVersion:t.attempt,status:"ready",expected:n,generated:s,validation:{passed:!0,errors:[],warnings:[]},createdAt:t.startedAt,updatedAt:i}}o(Jo,"buildDesignManifest");function Ho(e){return e==="workflow.yml"||e==="views.yml"||e.startsWith("recipes/")||/^workflows\/[^/]+\/workflow\.ya?ml$/.test(e)||/^workflows\/[^/]+\/views\.ya?ml$/.test(e)||/^workflows\/[^/]+\/recipes\/.+\.(ts|js)$/.test(e)}o(Ho,"isDesignArtifactPath");function _r(e){return e.endsWith("workflow.yml")||e.endsWith("workflow.yaml")?"workflow":e.endsWith("views.yml")||e.endsWith("views.yaml")?"views":"recipe"}o(_r,"designArtifactKind");function zo(e){return _r(e)!=="views"}o(zo,"isRequiredDesignArtifact");async function Or(e,t,r){let s=new Date().toISOString();await rt(e,{projectId:e.id,taskId:t.id,designVersion:t.attempt,status:"invalid",expected:["workflow.yml"],generated:[],validation:{passed:!1,errors:[r],warnings:[]},createdAt:t.startedAt,updatedAt:s})}o(Or,"writeInvalidDesignManifest");import en from"path";import{loadSettings as tn,resolveConfigPath as rn,loadWorkflowTemplate as sn,initTable as on,ensureTableExists as nn,syncViewsFromDesign as an,runWithExecutor as cn,runWithDwsRunner as un,DwsError as pn,logger as dn}from"aitable-workflow-core";var Go={singleSelect:"single_select",multipleSelect:"multiple_select",date:"datetime"},Ko={Grid:"grid",FormDesigner:"form",Kanban:"kanban",Calendar:"calendar",Gantt:"gantt"};function Fr(e,t){let r=new Map,s=new Map((e.tracker.field_definitions??[]).map(c=>[c.name,c])),n=o((c,u,p="text",f)=>{if(!c?.trim()||r.has(c))return;let g=s.get(c);r.set(c,{id:Zo(c),name:c,type:Qo(g?.type??p),...g?.options??f?{options:g?.options??f}:{},group:u})},"addField");for(let c of t.referencedFields.userInput)n(c,"business");for(let c of t.referencedFields.output)n(c,"business");for(let c of t.referencedFields.other)n(c,"business");for(let c of e.tracker.field_definitions??[])n(c.name,"business",c.type,c.options);n(e.tracker.fields.title,"business"),n(e.tracker.fields.description,"business"),n(e.tracker.fields.state,"process","singleSelect",Yo(e)),n(e.tracker.fields.current_step,"process"),n(e.tracker.fields.owner,"process","user"),n(e.tracker.fields.deadline,"process","date"),n(e.tracker.fields.event_log,"process");let i=Vo(e.tracker.views??[],t).map(c=>({id:c.id,name:c.name,kind:Ko[c.type]??"grid",desc:c.description??Xo(c)})),a=[...r.values()];return{fieldCount:a.length,viewCount:i.length,fields:a,views:i}}o(Fr,"buildSetupSchemaSummary");function Vo(e,t){let r=[],s=2;for(let n of e){if(!n.for_each_step){r.push(n);continue}let i=t.steps.filter(a=>n.for_each_step==="needs_human"?!!a.needs_human:!0);for(let a of i){let c=String.fromCodePoint(9311+s);r.push({...n,id:`step_${a.id}`,name:`${c} ${a.name}`,visible_fields:[...Object.values(a.input_mapping??{}),...Object.values(a.output_mapping??{})],description:a.needs_human?.reason??n.description,for_each_step:void 0}),s++}}return r}o(Vo,"expandViews");function Yo(e){return[e.tracker.states.initial,...e.tracker.states.active,...e.tracker.states.terminal,e.tracker.states.blocked,e.tracker.states.review].filter((t,r,s)=>!!t&&s.indexOf(t)===r)}o(Yo,"stateOptions");function Qo(e){return Go[e]??e}o(Qo,"displayFieldType");function Zo(e){return`fld_${e.replace(/[^\p{L}\p{N}]+/gu,"_").replace(/^_+|_+$/g,"")||"field"}`}o(Zo,"fieldId");function Xo(e){return e.type==="FormDesigner"?"\u8868\u5355\u5F55\u5165\u89C6\u56FE":e.type==="Kanban"?e.group_by?`\u6309 ${e.group_by} \u5206\u7EC4`:"\u770B\u677F\u89C6\u56FE":e.type==="Calendar"?e.date_field?`\u6309 ${e.date_field} \u67E5\u770B`:"\u65E5\u5386\u89C6\u56FE":e.type==="Gantt"?"\u7518\u7279\u89C6\u56FE":"\u8868\u683C\u89C6\u56FE"}o(Xo,"viewDescription");async function pt(e,t,r,s,n){let i=await E(t,e);try{n.ensureAvailable()}catch(d){S(s,pe(d),B(d),x(d));return}let a=await r.getState(i),c=q(a),u=c?.stage==="setup"?c:null;if(u?.status==="running"){Q(s),se(s,"task.snapshot",{type:"task.snapshot",task:u}),s.end();return}let p=u?.status==="blocked"?await r.updateTask(i,u.id,{status:"running",currentAction:"\u91CD\u65B0\u68C0\u67E5\u6388\u6743\u5E76\u7EE7\u7EED\u642D\u5EFA",block:void 0,error:void 0}):await r.createTask(i,"setup");Q(s);let f=o(d=>se(s,d.type,d),"send"),g=o(async(d,k)=>{let y=await r.updateTask(i,p.id,{currentAction:d,checkpoint:k});f({type:"task.progress",taskId:y.id,message:d,checkpoint:k}),f({type:"setup.progress",message:d})},"progress"),v=n.buildSetupExecutor({projectId:i.id,projectDir:i.dir}),P=n.buildDwsRunner();try{await cn(v,async()=>{await un(P,async()=>{f({type:"task.started",task:p}),f({type:"setup.started",projectId:e}),await g("\u52A0\u8F7D\u914D\u7F6E\u4E0E\u5DE5\u4F5C\u6D41\u6A21\u677F","preflight"),await an({viewsPath:en.resolve(i.dir,"views.yml"),configPath:J(i),autoUpdate:!0}).catch(F=>(dn.warn("setup","setup \u524D\u540C\u6B65 views.yml \u5931\u8D25\uFF0C\u7EE7\u7EED\u4F7F\u7528 config \u4E2D\u7684\u89C6\u56FE\u914D\u7F6E",{error:x(F)}),null));let d=await tn(J(i),{lenientEnv:!0}),k=await sn(d.workflowTemplatePath,{envSource:d.envSource}),y=d.tracker.base_id==="__placeholder__"?void 0:d.tracker,_=y??k.tracker,ae=!!_&&!_.table_id.trim();if(_&&!_.table_id.trim()){let F=y?await rn(J(i)):d.workflowTemplatePath,Oe=y?d:{...d,tracker:_},Pe=await nn({trackerFilePath:F,settings:Oe,workflow:k,onProgress:o((ke,oe)=>g(ke,oe),"onProgress")});d={...d,tracker:Pe.tracker}}await g("\u5F00\u59CB\u521B\u5EFA\u5B57\u6BB5\u548C\u89C6\u56FE","sync_schema");let ce=await on({settings:d,workflow:k,cleanEmptyRows:!0,tableJustCreated:ae});for(let F of ce.warnings)await g(`\u6CE8\u610F\uFF1A${F.message}`,"sync_schema");let ue=Fr(d,k),Z=ce.warnings.map(F=>F.message);await L(i);let H=await r.updateTask(i,p.id,{status:"succeeded",currentAction:"AI \u8868\u683C\u642D\u5EFA\u5B8C\u6210",checkpoint:"done",outputs:{tableUrl:st(d.tracker.base_id,d.tracker.table_id),baseId:d.tracker.base_id,tableId:d.tracker.table_id,schema:ue,warnings:Z}});f({type:"task.succeeded",task:H}),f({type:"setup.done",tableUrl:st(d.tracker.base_id,d.tracker.table_id),baseId:d.tracker.base_id,tableId:d.tracker.table_id,schema:ue,warnings:Z}),s.end()})})}catch(d){if(ln(d)){let y=x(d),_=await r.updateTask(i,p.id,{status:"blocked",checkpoint:"preflight",currentAction:"\u7B49\u5F85 AI \u8868\u683C\u6388\u6743",block:{reason:"auth_required",message:`\u6388\u6743\u6216\u6743\u9650\u4E0D\u8DB3\uFF1A${y}`,actionLabel:"\u6211\u5DF2\u5B8C\u6210\u6388\u6743\uFF0C\u7EE7\u7EED\u642D\u5EFA",help:"\u8BF7\u6267\u884C dws auth login\uFF0C\u5E76\u786E\u8BA4\u5F53\u524D\u8D26\u53F7\u53EF\u8BBF\u95EE\u76EE\u6807 Base/Table\u3002"}});f({type:"task.blocked",task:_}),s.end();return}let k=await r.updateTask(i,p.id,{status:"failed",currentAction:"AI \u8868\u683C\u642D\u5EFA\u5931\u8D25",error:{code:B(d),message:x(d),retryable:!0,details:U(d)}});f({type:"task.failed",task:k}),f({type:"error",error:me(d)}),s.end()}}o(pt,"handleSetup");function ln(e){let t=x(e).toLowerCase(),r=e instanceof pn?e.stderr.toLowerCase():"",s=`${t} ${r}`;return["auth","login","permission","unauthorized","forbidden","access denied"].some(n=>s.includes(n))}o(ln,"isSetupAuthBlocker");var I=class{ctx;serverServices;async taskAction(){this.ctx.respond=!1;let t=this.serverServices,r=this.ctx.state.project,s=this.ctx.params.stage??"",n=this.ctx.params.taskAction??"",i=n==="start"||n==="continue"||n==="retry";if(i&&s==="setup"){await b(r,t),await pt(r.id,t.workspace,t.projectStore.instances,this.ctx.res,t.execution);return}if(i&&s==="run"){await b(r,t),await ye(t).start(r.id,this.ctx.res);return}S(this.ctx.res,404,"NOT_FOUND","\u63A5\u53E3\u4E0D\u5B58\u5728")}async designDiscovery(){this.ctx.respond=!1;let t=this.serverServices,r=this.ctx.state.project;await b(r,t),await br(r.id,t.workspace,t.projectStore.instances,this.ctx.res,t.execution)}async designDiscoveryConfirm(){this.ctx.respond=!1;let t=this.serverServices,r=this.ctx.state.project;await b(r,t);let s=await W(this.ctx.req);await Ar(r.id,t.workspace,t.projectStore.instances,s,this.ctx.res)}async design(){this.ctx.respond=!1;let t=this.serverServices,r=this.ctx.state.project;await b(r,t);let s=new URL(this.ctx.req.url??"/","http://localhost"),n=Dt(s,this.ctx.req);await Er(r.id,t.workspace,t.projectStore.instances,this.ctx.res,t.execution,n),await Se(t,r,"design")}async setup(){this.ctx.respond=!1;let t=this.serverServices,r=this.ctx.state.project;await b(r,t),await pt(r.id,t.workspace,t.projectStore.instances,this.ctx.res,t.execution),await Se(t,r,"setup")}async runHumanAction(){this.ctx.respond=!1;let t=this.serverServices,r=this.ctx.state.project;await b(r,t),await Jt(r.id,t.workspace,t.projectStore.instances,t.runtime,this.ctx.params.recordId??"",this.ctx.req,this.ctx.res)}async run(){this.ctx.respond=!1;let t=this.serverServices,r=this.ctx.state.project;await b(r,t),await ye(t).start(r.id,this.ctx.res)}async runStop(){let t=await ye(this.serverServices).stop(this.ctx.state.project.id);this.ctx.respond=!1,m(this.ctx.res,t)}async runStatus(){let t=await ye(this.serverServices).status(this.ctx.state.project.id);this.ctx.respond=!1,m(this.ctx.res,t)}async runRecords(){let t=Number(Re(this.ctx.query?.limit)),r=Number.isInteger(t)&&t>0?t:void 0,s=await ye(this.serverServices).listRecords(this.ctx.state.project.id,{limit:r});this.ctx.respond=!1,m(this.ctx.res,s)}};o(I,"StagesController"),l([Nr()],I.prototype,"ctx",2),l([Nr()],I.prototype,"serverServices",2),l([ne("/:projectId/tasks/:stage/:taskAction")],I.prototype,"taskAction",1),l([ne("/:projectId/design/discovery")],I.prototype,"designDiscovery",1),l([ne("/:projectId/design/discovery/confirm")],I.prototype,"designDiscoveryConfirm",1),l([ne("/:projectId/design")],I.prototype,"design",1),l([ne("/:projectId/setup")],I.prototype,"setup",1),l([ne("/:projectId/run/records/:recordId/human-action")],I.prototype,"runHumanAction",1),l([ne("/:projectId/run")],I.prototype,"run",1),l([ne("/:projectId/run/stop")],I.prototype,"runStop",1),l([Wr("/:projectId/run/status")],I.prototype,"runStatus",1),l([Wr("/:projectId/run/records")],I.prototype,"runRecords",1),I=l([fn("/api/projects")],I);import*as Ve from"@midwayjs/core";import*as Nn from"@midwayjs/koa";import{createApp as En,logger as _n}from"aitable-workflow-core";var $e=class{static{o(this,"LocalProjectRepository")}workspace;constructor(t){this.workspace=t}async create(t,r){let s=await Et(this.workspace),n=[];if(r?.files&&r.files.length>0){let i=await Fe(s,r.files);n.push(...i.warnings);let a=await L(s);return r.title&&r.title.trim()&&(a=await we(a,{title:r.title.trim()})),{...a,files:i.written,warnings:n}}if(r?.title&&r.title.trim()){let i=await we(s,{title:r.title.trim()});return n.length?{...i,warnings:n}:{...i}}return n.length?{...s,warnings:n}:{...s}}async list(t){return tt(this.workspace)}async resolve(t,r){return E(this.workspace,t)}async updateMeta(t,r,s){return we(t,r)}async delete(t,r){await Ie(t)}async updateFiles(t,r,s){return r.remove&&r.remove.length>0&&await sr(t,r.remove),r.upsert&&r.upsert.length>0&&await Fe(t,r.upsert),await L(t),R(t)}async replaceFiles(t,r,s){return await or(t,r),await L(t),R(t)}async exportProject(t,r){return nr(t)}async syncHotFields(){}};import{randomUUID as mn}from"crypto";import{mkdir as Lr,readFile as Mr,rm as gn,writeFile as Cr}from"fs/promises";import de from"path";import{applyRunExecutionEvent as wn}from"aitable-workflow-core";var Be=new Map,hn=/^[a-zA-Z0-9_-]+$/,qe=class{static{o(this,"LocalWorkflowInstanceStore")}workspace;constructor(t){this.workspace=t}getState(t){return le(t)}getRecoverableState(t){return Dn(t)}createTask(t,r){return Pn(t,r)}updateTask(t,r,s){return kn(t,r,s)}clearActiveTask(t,r){return jn(t,r)}appendRunSnapshotEvent(t,r){return Tn(t,r)}updateRunSnapshotRecord(t,r,s,n){return Sn(t,r,s,n)}deleteProjectState(t){return yn(this.workspace,t)}};async function yn(e,t){lt(t);let r=vn(e,t);await gn(de.join(r,"runtime"),{recursive:!0,force:!0})}o(yn,"deleteProjectState");function vn(e,t){return lt(t),de.join(e.projectsRoot,t)}o(vn,"projectDir");async function Pn(e,t){return ve(e,async()=>{await Je(e);let r=await le(e),s=(r.stages[t]?.task.attempt??0)+1,n=new Date().toISOString(),i={id:`task_${mn().slice(0,8)}`,projectId:e.id,stage:t,status:"running",attempt:s,startedAt:n,updatedAt:n,currentAction:"\u4EFB\u52A1\u5DF2\u542F\u52A8"};return await Ue(e,i),await ie(e,We(r,i)),i})}o(Pn,"createTask");async function kn(e,t,r){return ve(e,async()=>{let s=await On(e,t),n=new Date().toISOString(),i={...s,...r,id:s.id,projectId:s.projectId,stage:s.stage,attempt:s.attempt,startedAt:s.startedAt,updatedAt:n,completedAt:r.completedAt??(Ne(r.status)?n:s.completedAt),block:Object.prototype.hasOwnProperty.call(r,"block")?r.block:Ne(r.status)?void 0:s.block};await Ue(e,i);let a=await le(e);return await ie(e,We(a,i)),i})}o(kn,"updateTask");async function le(e){await Je(e);let t=await Mr(Br(e),"utf8").catch(()=>"");if(t.trim()){let r=Rn(t),s=Y(e,r);return r.repaired&&await ie(e,s),s}return Y(e,{projectId:e.id,activeStage:null,stages:{},updatedAt:e.updatedAt})}o(le,"getState");async function Tn(e,t){return ve(e,async()=>{let r=await le(e),s=wn(r.runSnapshot??{records:[],logs:[]},t),n=Y(e,{...r,runSnapshot:s,updatedAt:new Date().toISOString()});return await ie(e,n),n})}o(Tn,"appendRunSnapshotEvent");async function Sn(e,t,r,s){return ve(e,async()=>{let n=await le(e);if(!n.runSnapshot)return n;let i=Date.now(),a={records:In(n.runSnapshot.records,t,u=>({...u,...r,updatedAt:i})),logs:s?An(n.runSnapshot.logs,t,r.currentStepId??"run","info",s):n.runSnapshot.logs},c=Y(e,{...n,runSnapshot:a,updatedAt:new Date().toISOString()});return await ie(e,c),c})}o(Sn,"updateRunSnapshotRecord");async function Dn(e){return ve(e,async()=>{let t=await le(e),r=t.stages.design?.task,s=await Ft(e);if(s.status==="ready"){if(!r||r.status==="succeeded")return t;let c=new Date().toISOString(),u={...r,status:"succeeded",updatedAt:c,completedAt:r.completedAt??c,currentAction:"\u5DE5\u4F5C\u6D41\u8BBE\u8BA1\u5B8C\u6210",checkpoint:"done",block:void 0,error:void 0};await Ue(e,u);let p=Y(e,{...t,activeStage:t.activeStage==="design"?null:t.activeStage,stages:{...t.stages,design:xe(u)},updatedAt:c});return await ie(e,p),p}if(r?.status!=="succeeded")return t;let n=new Date().toISOString(),i={...r,status:"failed",updatedAt:n,completedAt:n,currentAction:s.status==="artifact_missing"?"\u8BBE\u8BA1\u4EA7\u7269\u7F3A\u5931\uFF0C\u7B49\u5F85\u91CD\u65B0\u751F\u6210":"\u8BBE\u8BA1\u4EA7\u7269\u72B6\u6001\u5F02\u5E38\uFF0C\u7B49\u5F85\u91CD\u65B0\u751F\u6210",checkpoint:s.status,error:{code:s.status==="artifact_missing"?"ARTIFACT_MISSING":"DESIGN_ARTIFACT_INVALID",message:s.errors.join("\uFF1B")||"\u8BBE\u8BA1\u4EA7\u7269\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u5DE5\u4F5C\u6D41\u8BBE\u8BA1\u3002",retryable:!0,details:{missing:s.missing}}},a=Y(e,{...t,activeStage:t.activeStage==="design"?null:t.activeStage,stages:{...t.stages,design:xe(i)},updatedAt:n});return await Ue(e,i),await ie(e,a),a})}o(Dn,"getRecoverableState");async function jn(e,t){return ve(e,async()=>{let r=await le(e);if(q(r)?.id!==t)return r;let s=Y(e,{...r,activeStage:null,updatedAt:new Date().toISOString()});return await ie(e,s),s})}o(jn,"clearActiveTask");function dt(e){return de.join(e.dir,"runtime")}o(dt,"runtimeDir");function xn(e){return de.join(dt(e),"artifacts")}o(xn,"artifactsDir");function $r(e){return de.join(dt(e),"tasks")}o($r,"tasksDir");function Br(e){return de.join(dt(e),"state.json")}o(Br,"statePath");function qr(e,t){return lt(t),de.join($r(e),`${t}.json`)}o(qr,"taskPath");async function Je(e){await Lr($r(e),{recursive:!0}),await Lr(xn(e),{recursive:!0})}o(Je,"ensureRuntimeDirs");async function On(e,t){let r=await Mr(qr(e,t),"utf8");return JSON.parse(r)}o(On,"readTask");async function Ue(e,t){await Je(e),await Cr(qr(e,t.id),`${JSON.stringify(t,null,2)}
16
+ `,"utf8")}o(Ue,"writeTask");async function ie(e,t){await Je(e),await Cr(Br(e),`${JSON.stringify(t,null,2)}
17
+ `,"utf8")}o(ie,"writeProjectState");async function ve(e,t){let r=e.dir||e.id,n=(Be.get(r)??Promise.resolve()).catch(()=>{}).then(t),i=n.then(()=>{},()=>{});Be.set(r,i);try{return await n}finally{Be.get(r)===i&&Be.delete(r)}}o(ve,"withProjectStateWrite");function Rn(e){try{return JSON.parse(e)}catch(t){let r=bn(e);if(!r)throw t;return{...JSON.parse(r),repaired:!0}}}o(Rn,"parseProjectStateJson");function bn(e){let t=e.indexOf("{");if(t<0)return null;let r=0,s=!1,n=!1;for(let i=t;i<e.length;i+=1){let a=e[i];if(s){n?n=!1:a==="\\"?n=!0:a==='"'&&(s=!1);continue}if(a==='"')s=!0;else if(a==="{")r+=1;else if(a==="}"&&(r-=1,r===0))return e.slice(t,i+1)}return null}o(bn,"extractFirstJsonObject");function In(e,t,r){return e.map(s=>s.id===t?r(s):s)}o(In,"updateRunRecord");function An(e,t,r,s,n){return[...e,{id:`run_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,7)}`,ts:Date.now(),itemId:t,stepId:r,level:s,text:n}].slice(-300)}o(An,"appendRunLog");function lt(e){if(!hn.test(e))throw Object.assign(new Error(`\u975E\u6CD5\u9879\u76EE ID: ${e}`),{code:"INVALID_PROJECT_ID"})}o(lt,"assertProjectId");var He=class{static{o(this,"LocalProjectStore")}projects;instances;workflows;constructor(t){this.projects=new $e(t),this.instances=new qe(t)}async prepareWorkdir(t,r){return{project:await L(t),materialized:0,warnings:[]}}async commitWorkdir(t,r){}async cascadeDelete(t){}};var ze=class{static{o(this,"LocalExecutionBackend")}kind="local";ensureAvailable(){}async buildDesignExecutor(){}buildSetupExecutor(){}buildDwsRunner(){}listMachines(){}isMachineOnline(){return!1}async getProjectMachine(){return null}async bindProjectMachine(){throw Object.assign(new Error("\u5F15\u64CE\u7BA1\u7406\u6682\u4E0D\u53EF\u7528"),{code:"NOT_AVAILABLE",status:503})}async unbindProjectMachine(){}};var Ge=class{static{o(this,"LocalIdentityService")}authRequired=!1;authenticate(t){return gt(t)}async resolveScope(t){let r=t.headers["x-actor-id"],s=Array.isArray(r)?r[0]:r;return s?.trim()?{actorId:s.trim()}:{}}};function Ur(e={}){let t=e.workspace??At({dataRoot:e.dataRoot});return{workspace:t,runtime:ur(e.appFactory??((r,s)=>En(r,{onRunEvent:s}))),projectStore:e.projectStore??new He(t),execution:e.execution??new ze,identityService:e.identityService??new Ge,webRoot:e.webRoot}}o(Ur,"createServerServices");function Jr(e=process.env){let t=e.AITABLE_RELAY_BOOTSTRAP_KEY;return typeof t=="string"&&t.trim()!==""?(_n.warn("server","AITABLE_RELAY_BOOTSTRAP_KEY \u5DF2\u5E9F\u5F03\u5E76\u88AB\u5FFD\u7565\uFF1Abootstrap \u8BA4\u8BC1\u5DF2\u79FB\u9664\uFF0C\u8BF7\u6539\u7528 owner-token\uFF08sk_owner_*\uFF09\u6216 per-machine key\uFF08sk_machine_*\uFF09"),!0):!1}o(Jr,"warnDeprecatedBootstrapKey");var Fn="127.0.0.1",Ou=8787;function Ke(e){if(typeof e!="string")return;let t=e.trim();return t||void 0}o(Ke,"readEnvString");function Wn(e){let t=Ke(e);if(t===void 0)return;let r=Number.parseInt(t,10);return Number.isFinite(r)&&r>0?r:void 0}o(Wn,"readEnvPort");function Hr(e={},t=process.env){let r=e.host??Ke(t.AITABLE_WORKFLOW_HOST)??Fn,s=e.port??Wn(t.AITABLE_WORKFLOW_PORT)??8787,n=e.dataRoot??Ke(t.AITABLE_WORKFLOW_DATA_DIR)??void 0,i=e.webRoot??Ke(t.AITABLE_WORKFLOW_WEB_ROOT)??void 0;return{host:r,port:s,dataRoot:n,webRoot:i}}o(Hr,"resolveServeConfig");async function Eu(e={}){Jr();let t=Hr({port:e.port,host:e.host,dataRoot:e.dataRoot,webRoot:e.webRoot}),r=e.services??Ur({dataRoot:t.dataRoot,appFactory:e.appFactory,webRoot:t.webRoot}),s=await Ln(r,t.host,t.port);return console.log(`aitable-workflow server listening on http://${t.host}:${t.port}`),console.log(`data root: ${r.workspace.dataRoot}`),s}o(Eu,"startServer");async function Ln(e,t,r){let s=await Mn(()=>Ve.initializeGlobalApplicationContext({imports:[Nn,Ce],moduleDetector:!1,globalConfig:{keys:["aitable-workflow"],koa:{port:r,hostname:t},bodyParser:{enable:!1}}}));s.registerObject("serverServices",e);let n=await s.getAsync(Ve.HTTP_SERVER_KEY);return await Cn(n),n}o(Ln,"bootMidwayKoa");async function Mn(e){let t=process.env.MIDWAY_HTTP_PORT;delete process.env.MIDWAY_HTTP_PORT;try{return await e()}finally{t===void 0?delete process.env.MIDWAY_HTTP_PORT:process.env.MIDWAY_HTTP_PORT=t}}o(Mn,"withoutLeakedMidwayPortEnv");function Cn(e){return e.listening?Promise.resolve():new Promise((t,r)=>{let s=o(()=>{e.removeListener("error",n),t()},"onListening"),n=o(i=>{e.removeListener("listening",s),r(i)},"onError");e.once("listening",s),e.once("error",n)})}o(Cn,"waitUntilListening");import zr from"path";function Nu(e){let t=zr.normalize(e).split(zr.sep).join("/");return t==="workflow.yml"?"workflow":t==="views.yml"?"views":t.startsWith("recipes/")?"recipe":"other"}o(Nu,"workflowFileKind");var Gr=class{static{o(this,"LocalProjectMutexStore")}queues=new Map;withProjectLock(t,r){let n=(this.queues.get(t)??Promise.resolve()).then(r,r);return this.queues.set(t,n),n.then(()=>{this.queues.get(t)===n&&this.queues.delete(t)},()=>{this.queues.get(t)===n&&this.queues.delete(t)}).catch(()=>{}),n}};export{Fn as DEFAULT_SERVE_HOST,Ou as DEFAULT_SERVE_PORT,ct as LeaseRunController,ze as LocalExecutionBackend,Ge as LocalIdentityService,Gr as LocalProjectMutexStore,$e as LocalProjectRepository,He as LocalProjectStore,at as LocalRunController,qe as LocalWorkflowInstanceStore,q as activeTaskOf,me as apiError,Xt as assertSafeProjectPath,Zt as assertSafeRelativePath,ma as assertSafeWorkflowPath,Qs as assertWritableKind,st as buildAITableUrl,jt as cascadeDeleteProject,De as checksumContent,Se as commitStageOnTerminal,J as configPath,Et as createProject,ur as createServerRuntime,Ur as createServerServices,At as createWorkspace,Ie as deleteProject,B as errorCode,U as errorDetails,x as errorMessage,pe as errorStatus,Re as firstQueryValue,pr as handleRunRuntime,er as isForbiddenWritable,Ts as isStageActiveFor,Ne as isTerminalStatus,tr as kindOf,R as listProjectFiles,tt as listProjects,We as mergeTaskIntoState,Ce as midwayComponent,Y as normalizeProjectState,dr as overlayRunSessionState,b as prepareWorkdirGuarded,Rs as readDesignManifest,W as readJson,sr as removeProjectFiles,or as replaceProjectFiles,_t as resetProjectWorkflow,E as resolveProject,ye as resolveRunController,Hr as resolveServeConfig,A as scopeOf,S as sendError,m as sendJson,yt as sendText,vt as sendUnauthorized,xe as stageStateOf,Eu as startServer,L as touchProject,we as updateProjectMeta,Zs as validateConfigScene,Ft as validateDesignArtifacts,Jr as warnDeprecatedBootstrapKey,Mn as withoutLeakedMidwayPortEnv,Nu as workflowFileKind,rt as writeDesignManifest,Fe as writeProjectFiles};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aitable-workflow-server",
3
- "version": "0.1.17",
4
- "description": "aitable-workflow 服务端(server):纯 local 的 HTTP design/setup/run API(Hono)。relay gateway/PG/OSS 装配在 apps/gateway。",
3
+ "version": "0.1.18-beta.1786440467350",
4
+ "description": "aitable-workflow 服务端(server):纯 local 的 HTTP design/setup/run API(Midway component)。relay gateway/PG/OSS 装配在 apps/gateway。",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -22,9 +22,9 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "aitable-workflow-core": "0.1.17",
26
- "@hono/node-server": "^2.0.12",
27
- "hono": "^4.12.32",
25
+ "aitable-workflow-core": "0.1.18-beta.1786440467350",
26
+ "@midwayjs/core": "^3.20.4",
27
+ "@midwayjs/koa": "^3.20.4",
28
28
  "yaml": "^2.8.1"
29
29
  }
30
30
  }