aitable-workflow-server 0.1.8

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.
@@ -0,0 +1,574 @@
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, ApiError } from 'aitable-workflow-core';
3
+ import { Context, Hono } from 'hono';
4
+
5
+ type WorkspaceOptions = {
6
+ dataRoot?: string;
7
+ };
8
+ type Workspace = {
9
+ dataRoot: string;
10
+ projectsRoot: string;
11
+ };
12
+ declare function createWorkspace(options?: WorkspaceOptions): Workspace;
13
+ declare function createProject(workspace: Workspace): Promise<ProjectDTO>;
14
+ declare function updateProjectMeta(project: ProjectDTO, patch: Partial<Pick<ProjectDTO, "title">>): Promise<ProjectDTO>;
15
+ declare function listProjects(workspace: Workspace): Promise<ProjectDTO[]>;
16
+ declare function resolveProject(workspace: Workspace, projectId: string): Promise<ProjectDTO>;
17
+ declare function touchProject(project: ProjectDTO): Promise<ProjectDTO>;
18
+ declare function resetProjectWorkflow(project: ProjectDTO): Promise<ProjectDTO>;
19
+ /**
20
+ * WP-10-001:删除整个项目目录(rm -rf)。
21
+ *
22
+ * 本地状态文件(runtime/state.json、tasks 等)随之删除;DB 投影(project_state/
23
+ * project_machine 行)的级联清理由 server.ts 路由在删除前/后调用对应 repository。
24
+ */
25
+ declare function deleteProject(project: ProjectDTO): Promise<void>;
26
+ declare function readDesignManifest(project: ProjectDTO): Promise<DesignArtifactManifestDTO | null>;
27
+ declare function writeDesignManifest(project: ProjectDTO, manifest: DesignArtifactManifestDTO): Promise<void>;
28
+ declare function validateDesignArtifacts(project: ProjectDTO): Promise<DesignArtifactValidationDTO>;
29
+ declare function checksumContent(content: string): string;
30
+ declare function listProjectFiles(project: ProjectDTO): Promise<ProjectFileDTO[]>;
31
+ declare function configPath(project: ProjectDTO): string;
32
+ declare function buildAITableUrl(baseId: string, sheetId: string, viewId?: string): string;
33
+
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 function createLocalWorkflowInstanceStore(workspace: Workspace): WorkflowInstanceStore;
50
+
51
+ type RunExecutionEvent = Parameters<RunEventSink>[0];
52
+
53
+ type RunAppFactory = (configPath: string, onRunEvent?: (event: RunExecutionEvent) => void) => Promise<App>;
54
+ type RunSession = {
55
+ taskId: string;
56
+ subscribers: Set<ServerResponse>;
57
+ events: RunEventDTO[];
58
+ app: App | null;
59
+ snapshotWrite: Promise<unknown>;
60
+ };
61
+ type ServerRuntime = {
62
+ appFactory: RunAppFactory;
63
+ runSessions: Map<string, RunSession>;
64
+ };
65
+ type RunSseWriter = (res: ServerResponse, event: string, data: unknown) => void;
66
+ declare function createServerRuntime(appFactory: RunAppFactory): ServerRuntime;
67
+ declare function handleRunRuntime(projectId: string, workspace: Workspace, projectStateStore: WorkflowInstanceStore, runtime: ServerRuntime, res: ServerResponse, writeSse: RunSseWriter): Promise<void>;
68
+ declare function overlayRunSessionState(state: ProjectStateDTO, session: RunSession | undefined): ProjectStateDTO;
69
+
70
+ type ActorContext = {
71
+ actorId?: string;
72
+ /**
73
+ * WP-10-002:项目 owner 隔离用。relay 模式由 owner-token 派生,local 模式可空。
74
+ * 004 接入鉴权后由路由层注入;本字段仅由 repository 存取(owner_user_id 列)。
75
+ */
76
+ ownerUserId?: string;
77
+ };
78
+ /** 带内容创建项目的初始载荷。 */
79
+ type CreateProjectInit = {
80
+ title?: string;
81
+ files?: ProjectFileDTO[];
82
+ };
83
+ /** 增量写文件补丁。 */
84
+ type FilesPatch = {
85
+ upsert?: ProjectFileDTO[];
86
+ remove?: string[];
87
+ };
88
+ /** 创建结果:项目 + 写入的文件 + warnings(files/warnings 仅在带内容创建时出现)。 */
89
+ type CreateProjectResult = ProjectDTO & {
90
+ files?: ProjectFileDTO[];
91
+ warnings?: string[];
92
+ };
93
+ interface ProjectRepository {
94
+ create(actor?: ActorContext, init?: CreateProjectInit): Promise<CreateProjectResult>;
95
+ list(actor?: ActorContext): Promise<ProjectDTO[]>;
96
+ resolve(projectId: string, actor?: ActorContext): Promise<ProjectDTO>;
97
+ updateMeta(project: ProjectDTO, patch: Partial<Pick<ProjectDTO, "title">>, actor?: ActorContext): Promise<ProjectDTO>;
98
+ /** WP-10-001:删除项目目录(rm -rf)。DB 投影级联由路由层负责。 */
99
+ delete(project: ProjectDTO, actor?: ActorContext): Promise<void>;
100
+ /** WP-10-001:增量写文件(upsert/remove)。返回写入后的项目文件清单。 */
101
+ updateFiles(project: ProjectDTO, patch: FilesPatch, actor?: ActorContext): Promise<ProjectFileDTO[]>;
102
+ /** WP-10-001:整包替换文件。返回写入后的项目文件清单。 */
103
+ replaceFiles(project: ProjectDTO, files: ProjectFileDTO[], actor?: ActorContext): Promise<ProjectFileDTO[]>;
104
+ /** WP-10-001:导出项目(project + files + checksums + exportedAt)。 */
105
+ exportProject(project: ProjectDTO, actor?: ActorContext): Promise<ProjectExportDTO>;
106
+ /**
107
+ * WP-10-010:同步冗余热字段到 `projects` 行(scene/stage/table_base_id)。
108
+ *
109
+ * stage 流产物(scaffold/design/setup)直接写盘,DB 行的热字段需由磁盘 config.yml /
110
+ * project_state 反推同步,使 `projects` 表的冗余投影与磁盘权威源一致。三个字段全量覆盖:
111
+ * string → 写入该值;null → 清空(磁盘无该值)。可选方法:PG 仓库实现(UPDATE projects 行),
112
+ * local 模式 / 测试 fake 可省略({@link syncProjectFilesToDb} feature-detect,缺失则跳过热字段同步)。
113
+ */
114
+ syncHotFields?(project: ProjectDTO, patch: {
115
+ scene: string | null;
116
+ stage: string | null;
117
+ tableBaseId: string | null;
118
+ }, actor?: ActorContext): Promise<void>;
119
+ }
120
+ declare function createLocalProjectRepository(workspace: Workspace): ProjectRepository;
121
+
122
+ /** 带内容创建工作流的初始载荷。 */
123
+ type CreateWorkflowInit = {
124
+ name: string;
125
+ templatePath?: string;
126
+ files?: ProjectFileDTO[];
127
+ /**
128
+ * WP-10-015:工作流 table_id(来自 workflow.yml 的 tracker.table_id)。
129
+ * syncProjectFilesToDb 新建工作流时解析回填;缺省 → null(工作流无独立 table_id)。
130
+ */
131
+ tableId?: string | null;
132
+ };
133
+ /** 创建结果:工作流 + 写入的文件 + warnings(files/warnings 仅在带内容创建时出现)。 */
134
+ type CreateWorkflowResult = CreateWorkflowResponseDTO;
135
+ /** 单工作流导出包:workflow + files + checksums。 */
136
+ type WorkflowExportDTO = {
137
+ workflow: ProjectWorkflowDTO;
138
+ files: ProjectFileDTO[];
139
+ checksums: Array<{
140
+ path: string;
141
+ sha256: string;
142
+ }>;
143
+ };
144
+ interface WorkflowRepository {
145
+ list(projectId: string, actor?: ActorContext): Promise<ProjectWorkflowDTO[]>;
146
+ create(projectId: string, init: CreateWorkflowInit, actor?: ActorContext): Promise<CreateWorkflowResult>;
147
+ resolve(projectId: string, workflowId: string, actor?: ActorContext): Promise<ProjectWorkflowDTO>;
148
+ delete(workflow: ProjectWorkflowDTO, actor?: ActorContext): Promise<void>;
149
+ updateFiles(workflow: ProjectWorkflowDTO, patch: FilesPatch, actor?: ActorContext): Promise<ProjectFileDTO[]>;
150
+ replaceFiles(workflow: ProjectWorkflowDTO, files: ProjectFileDTO[], actor?: ActorContext): Promise<ProjectFileDTO[]>;
151
+ /**
152
+ * WP-10-015:回填/更新工作流 table_id(从 workflow.yml 的 tracker.table_id 解析)。
153
+ * tableId 传 null 表示该工作流无独立 table_id(单工作流用项目级 table_base_id)。
154
+ */
155
+ updateTableId(workflow: ProjectWorkflowDTO, tableId: string | null, actor?: ActorContext): Promise<void>;
156
+ /** 导出工作流(workflow + files + checksums);不存在或越权 → 404 WORKFLOW_NOT_FOUND。 */
157
+ exportWorkflow(projectId: string, workflowId: string, actor?: ActorContext): Promise<WorkflowExportDTO>;
158
+ }
159
+ /**
160
+ * 工作流内容文件分类(对齐 workflow_files.kind:workflow|recipe|views|other)。
161
+ * 与 project-files.kindOf 不同——此处 path 相对工作流目录(workflow.yml 在根)。
162
+ */
163
+ declare function workflowFileKind(relPath: string): "workflow" | "recipe" | "views" | "other";
164
+
165
+ /** prepareWorkdir(权威源 → FS 物化)结果。 */
166
+ type MaterializeResult = {
167
+ project: ProjectDTO;
168
+ /** 写入磁盘的内容文件数。 */
169
+ materialized: number;
170
+ /** 告警(stage 活跃跳过、受保护路径跳过、空库等)。 */
171
+ warnings: string[];
172
+ };
173
+ /** 权威源 → FS 物化选项(PG 实现用;local 忽略)。 */
174
+ type MaterializeOptions = {
175
+ /** stage(design/setup/run)活跃时跳过清空重写,保护 agent 进行中产物(WP-10-011 #1)。 */
176
+ skipIfStageActive?: boolean;
177
+ isStageActive?: () => boolean;
178
+ };
179
+ interface ProjectStore {
180
+ /** 项目 CRUD / 内容文件(FS 或 PG projects/project_files)。 */
181
+ readonly projects: ProjectRepository;
182
+ /** stage 任务与运行快照状态。 */
183
+ readonly instances: WorkflowInstanceStore;
184
+ /** 工作流子资源(WP-10-008)。local 无多工作流表 → undefined(路由 501)。 */
185
+ readonly workflows?: WorkflowRepository;
186
+ /**
187
+ * stage/写操作入口前置:保证 project.dir 与权威源对齐(PG:物化建盘;local:touch)。
188
+ * `isStageActive` 供 PG 实现跳过清空以保护 stage 进行中的 agent 写盘产物。
189
+ */
190
+ prepareWorkdir(project: ProjectDTO, opts?: {
191
+ isStageActive: () => boolean;
192
+ }): Promise<MaterializeResult>;
193
+ /**
194
+ * stage 终态后收口:把磁盘产物回写权威源(PG:disk→DB;local:no-op)。
195
+ * 终态判定与 best-effort 语义由 lifecycle 助手(commitStageOnTerminal)承担。
196
+ */
197
+ commitWorkdir(project: ProjectDTO, options: {
198
+ stage?: string | null;
199
+ }): Promise<void>;
200
+ /** 删除项目后的存储级联清理(state 行 / 绑定行等;local no-op,FS 已随目录删除)。 */
201
+ cascadeDelete(projectId: string): Promise<void>;
202
+ }
203
+ declare class LocalProjectStore implements ProjectStore {
204
+ readonly projects: ProjectRepository;
205
+ readonly instances: WorkflowInstanceStore;
206
+ readonly workflows?: WorkflowRepository;
207
+ constructor(workspace: Workspace);
208
+ prepareWorkdir(project: ProjectDTO, _opts?: {
209
+ isStageActive: () => boolean;
210
+ }): Promise<MaterializeResult>;
211
+ commitWorkdir(_project: ProjectDTO, _options?: {
212
+ stage?: string | null;
213
+ }): Promise<void>;
214
+ cascadeDelete(_projectId: string): Promise<void>;
215
+ }
216
+
217
+ /** 在线机器快照(供路由 / 账号中心展示;relay gateway 侧生产,接口层只读)。 */
218
+ interface RelayMachineInfo {
219
+ machineId: string;
220
+ runtimes: string[];
221
+ hasDws: boolean;
222
+ hostname: string;
223
+ os: string;
224
+ /** 认证出的属主 userId(若已知);供 per-owner 连接配额跨实例聚合。 */
225
+ ownerUserId?: string;
226
+ lastSeen: number;
227
+ }
228
+ interface ExecutionBackend {
229
+ readonly kind: "local" | "relay";
230
+ /**
231
+ * stage 入口前置校验:relay 无在线机器时抛 `NO_ONLINE_MACHINE`(status 503,
232
+ * 由调用方在启动 SSE 前转 JSON 错误);local 恒通过。
233
+ */
234
+ ensureAvailable(): void;
235
+ /**
236
+ * design 阶段执行器(绑定优先路由,异步解析机器)。
237
+ * local 返回 undefined → runWithExecutor 透明本机 spawn。
238
+ */
239
+ buildDesignExecutor(ctx: {
240
+ projectId: string;
241
+ agent: AgentType | undefined;
242
+ projectDir: string;
243
+ }): Promise<AgentExecutor | undefined>;
244
+ /**
245
+ * setup 阶段执行器(能力匹配,同步选择;无在线机器返回 undefined)。
246
+ */
247
+ buildSetupExecutor(ctx: {
248
+ projectId: string;
249
+ projectDir: string;
250
+ }): AgentExecutor | undefined;
251
+ /** setup 阶段 dws runner;local 返回 undefined → dws 本机 spawn。 */
252
+ buildDwsRunner(): DwsRunner | undefined;
253
+ /** 在线机器列表;local 无此能力(gateway 的 /api/relay/machines 用)。 */
254
+ listMachines(): RelayMachineInfo[] | undefined;
255
+ /** 指定机器是否在线;local 恒 false(无 relay 概念)。 */
256
+ isMachineOnline(machineId: string): boolean;
257
+ /** 项目当前绑定的执行机器;local 恒 null(无绑定概念,engine 路由回 unbound)。 */
258
+ getProjectMachine(projectId: string): Promise<string | null>;
259
+ /** 绑定项目到执行机器;local 抛 NOT_AVAILABLE(503,引擎管理不可用)。 */
260
+ bindProjectMachine(projectId: string, machineId: string): Promise<void>;
261
+ /** 解绑项目的执行机器(项目删除级联);local no-op。 */
262
+ unbindProjectMachine(projectId: string): Promise<void>;
263
+ }
264
+ declare class LocalExecutionBackend implements ExecutionBackend {
265
+ readonly kind: "local";
266
+ ensureAvailable(): void;
267
+ buildDesignExecutor(): Promise<AgentExecutor | undefined>;
268
+ buildSetupExecutor(): AgentExecutor | undefined;
269
+ buildDwsRunner(): DwsRunner | undefined;
270
+ listMachines(): RelayMachineInfo[] | undefined;
271
+ isMachineOnline(): boolean;
272
+ getProjectMachine(): Promise<string | null>;
273
+ bindProjectMachine(): Promise<void>;
274
+ unbindProjectMachine(): Promise<void>;
275
+ }
276
+
277
+ interface IdentityService {
278
+ /**
279
+ * 是否存在登录态(前端经 GET /api/public-config 的 `authEnabled` 感知):
280
+ * local 无账号体系 → false(前端跳过登录页);gateway → true。
281
+ */
282
+ readonly authRequired: boolean;
283
+ /** 请求是否放行(免鉴权路径由中间件先判定,见 http/cors-auth.isAuthExempt)。可异步(gateway 的 owner-token 校验需查库)。 */
284
+ authenticate(req: IncomingMessage): boolean | Promise<boolean>;
285
+ /** 解析仓库 actor(actorId 调试标识 + 可选 ownerUserId 多租户归属)。 */
286
+ resolveScope(req: IncomingMessage): Promise<ActorContext>;
287
+ }
288
+ declare class LocalIdentityService implements IdentityService {
289
+ readonly authRequired: boolean;
290
+ authenticate(req: IncomingMessage): boolean | Promise<boolean>;
291
+ resolveScope(req: IncomingMessage): Promise<ActorContext>;
292
+ }
293
+
294
+ /** 每请求绑定:原生 req/res、服务集合、请求级缓存(actor scope / 已解析 project)。 */
295
+ type HttpEnv = {
296
+ incoming: IncomingMessage;
297
+ outgoing: ServerResponse;
298
+ services: ServerServices;
299
+ scope: ActorContext;
300
+ project?: ProjectDTO;
301
+ /** 既有 raw handler 已直接写出响应(JSON/SSE),适配层跳过 Response 序列化。 */
302
+ rawHandled?: boolean;
303
+ };
304
+ type AppEnv = {
305
+ Bindings: HttpEnv;
306
+ };
307
+ type Ctx = Context<AppEnv>;
308
+ /** 与旧 sendJson 字节一致的 JSON 响应(尾部换行 + charset)。 */
309
+ declare function jsonBody(c: Ctx, body: unknown, status?: number): Response;
310
+ declare function errorBody(c: Ctx, status: number, code: string, message: string, details?: unknown): Response;
311
+ /** 与旧 sendUnauthorized 字节一致。 */
312
+ declare function unauthorized(c: Ctx): Response;
313
+ /** 包装既有 (raw req/res) handler:执行后标记 rawHandled,适配层不再写 Response。 */
314
+ declare function raw(fn: (c: Ctx) => Promise<void>): (c: Ctx) => Promise<Response>;
315
+ /** 执行域接缝。 */
316
+ declare function backendOf(c: Ctx): ExecutionBackend;
317
+ /** 单项目子树公共前置:按 :projectId + actor scope 解析项目,失败经 onError 统一映射。 */
318
+ declare function resolveProjectMw(c: Ctx, next: () => Promise<void>): Promise<void>;
319
+
320
+ /**
321
+ * 服务集合:**4 个业务接缝 + 2 个数据字段**,封口不再扩张——
322
+ * local/gateway 的任何行为分化必须归入 store / execution / identity / extendApp
323
+ * 之一(在对应领域服务上加方法,local 给基线实现),不允许新增顶层注入字段。
324
+ */
325
+ type ServerServices = {
326
+ workspace: Workspace;
327
+ runtime: ServerRuntime;
328
+ /** ① 存储域:项目/工作流/实例状态持久化 + 工作目录生命周期(local FS / gateway PG)。 */
329
+ projectStore: ProjectStore;
330
+ /** ② 执行域:design/setup 的 agent/dws 执行 + 项目↔机器绑定(local spawn / relay 投递)。 */
331
+ execution: ExecutionBackend;
332
+ /** ③ 身份域:请求鉴权 + actor 归属解析(local API token / gateway JWT+owner-token)。 */
333
+ identityService: IdentityService;
334
+ /** ④ 路由扩展:组合根追加自有路由(gateway 的 /api/auth /api/account /api/relay)。 */
335
+ extendApp?: (app: Hono<AppEnv>) => void;
336
+ /** 本地 web 静态资源目录;配置时 handleRequest 托管前端页面。 */
337
+ webRoot?: string;
338
+ };
339
+ type ServerServicesOptions = {
340
+ dataRoot?: string;
341
+ appFactory?: RunAppFactory;
342
+ /** 可选注入:预构建 workspace(组合根需先建 workspace 以对齐 PG projectsRoot)。 */
343
+ workspace?: Workspace;
344
+ /** 可选注入:存储域(缺省 LocalProjectStore)。 */
345
+ projectStore?: ProjectStore;
346
+ /** 可选注入:执行域(缺省 LocalExecutionBackend)。 */
347
+ execution?: ExecutionBackend;
348
+ /** 可选注入:身份域(缺省 LocalIdentityService)。 */
349
+ identityService?: IdentityService;
350
+ /** 可选注入:路由扩展。 */
351
+ extendApp?: (app: Hono<AppEnv>) => void;
352
+ /** 本地 web 静态资源目录。 */
353
+ webRoot?: string;
354
+ };
355
+ /**
356
+ * 纯 local 装配:零外部依赖(无 DB / Redis / OSS / relay),全部落本地文件系统。
357
+ * apps/gateway 作为组合根时经 options 注入 PG store / RelayExecutionBackend /
358
+ * GatewayIdentityService。
359
+ */
360
+ declare function createServerServices(options?: ServerServicesOptions): ServerServices;
361
+ /**
362
+ * 启动期一次性废弃告警:§3 已彻底移除 bootstrap 认证及其配置项。若部署环境仍配置了
363
+ * `AITABLE_RELAY_BOOTSTRAP_KEY`,打印 warn 提示"已废弃、被忽略",**不参与任何认证/装配逻辑**
364
+ * (认证只保留 owner-token `sk_owner_*` 与 per-machine key `sk_machine_*`)。
365
+ *
366
+ * @param env 环境变量来源,缺省 `process.env`(测试可注入)。
367
+ * @returns 是否发出了告警(便于单测断言)。
368
+ */
369
+ declare function warnDeprecatedBootstrapKey(env?: NodeJS.ProcessEnv): boolean;
370
+
371
+ /**
372
+ * server.ts —— 服务入口与 node:http ↔ Hono 适配层。
373
+ *
374
+ * - 路由与中间件在 {@link createHonoApp}(app.ts);本文件仅保留:
375
+ * startServer(监听 + 默认管理员种子)与 handleRequest(把原生 req/res 桥接进
376
+ * Hono 应用,供 http.createServer 与测试直调)。
377
+ * - 既有 raw handler(SSE / 直接写 res 的 JSON)经 env.rawHandled 标记跳过 Response 序列化。
378
+ * - 本包为纯 local server:relay 网关 / PG / OSS 的装配在 apps/gateway(组合根经
379
+ * options.services 注入完整 ServerServices,并自行挂载 WS upgrade)。
380
+ */
381
+
382
+ type StartServerOptions = {
383
+ port?: number;
384
+ host?: string;
385
+ dataRoot?: string;
386
+ appFactory?: RunAppFactory;
387
+ /** 本地 web 静态资源目录(local 模式托管前端页面);透传至 resolveServeConfig。 */
388
+ webRoot?: string;
389
+ /**
390
+ * 组合根注入的完整服务集合(apps/gateway 用:PG repos / RelayExecutionBackend 等)。
391
+ * 省略时按纯 local 装配(createServerServices)。
392
+ */
393
+ services?: ServerServices;
394
+ };
395
+ declare function startServer(options?: StartServerOptions): Promise<http.Server>;
396
+ /**
397
+ * node:http ↔ Hono 请求适配:
398
+ * 1. CORS / OPTIONS 预检与 local 静态托管在进入 Hono 前处理(与旧实现顺序一致)。
399
+ * 2. 由原生 req 构造 fetch Request(不消费 body:raw handler 仍从原生 req 读流)。
400
+ * 3. raw handler 已写出(env.rawHandled)→ 直接返回;否则把 Hono Response 序列化回 res。
401
+ */
402
+ declare function handleRequest(services: ServerServices, req: IncomingMessage, res: ServerResponse): Promise<void>;
403
+
404
+ /**
405
+ * serve 配置解析层
406
+ *
407
+ * 统一 serve 子命令的配置来源,优先级严格为:
408
+ * CLI 参数 > 环境变量 > 默认值
409
+ *
410
+ * env 命名对齐 docs/deployment/relay_arch.md §4.1。
411
+ * 本包为纯 local server,不再有 mode 概念(relay 部署 = apps/gateway 组合根)。
412
+ */
413
+ /** serve 默认监听地址(容器内可由 CMD 传 0.0.0.0 覆盖)。 */
414
+ declare const DEFAULT_SERVE_HOST = "127.0.0.1";
415
+ /** serve 默认监听端口。 */
416
+ declare const DEFAULT_SERVE_PORT = 8787;
417
+ /**
418
+ * 来自 CLI 的显式覆盖项。仅在用户显式提供时才应设置对应字段,
419
+ * 未提供的字段留空(undefined),以便回退到环境变量、再回退到默认值。
420
+ */
421
+ type ServeConfigOverrides = {
422
+ port?: number;
423
+ host?: string;
424
+ dataRoot?: string;
425
+ /** 本地 web 静态资源目录(local 模式托管前端页面)。 */
426
+ webRoot?: string;
427
+ };
428
+ /** 解析后的最终 serve 配置。 */
429
+ type ServeConfig = {
430
+ port: number;
431
+ host: string;
432
+ /** 未显式配置时为 undefined,交由 workspace 层回退到默认数据目录。 */
433
+ dataRoot?: string;
434
+ /** 本地 web 静态资源目录;未配置时为 undefined(不托管静态页面)。 */
435
+ webRoot?: string;
436
+ };
437
+ /**
438
+ * 按 CLI > env > 默认值 的优先级解析 serve 配置。
439
+ *
440
+ * @param overrides CLI 显式提供的覆盖项(未提供的字段应为 undefined)
441
+ * @param env 环境变量来源,默认 process.env(便于测试注入)
442
+ */
443
+ declare function resolveServeConfig(overrides?: ServeConfigOverrides, env?: NodeJS.ProcessEnv): ServeConfig;
444
+
445
+ declare function createHonoApp(): Hono<AppEnv>;
446
+
447
+ declare function readJson<T>(req: IncomingMessage): Promise<T>;
448
+ declare function sendJson(res: ServerResponse, body: unknown, status?: number): void;
449
+ declare function sendError(res: ServerResponse, status: number, code: string, message: string, details?: unknown): void;
450
+ declare function sendUnauthorized(res: ServerResponse): void;
451
+ declare function apiError(error: unknown): ApiError["error"];
452
+ declare function errorStatus(error: unknown): number;
453
+ declare function errorCode(error: unknown): string;
454
+ declare function errorMessage(error: unknown): string;
455
+ declare function errorDetails(error: unknown): unknown;
456
+
457
+ /** 文件分类,对齐 {@link import("aitable-workflow-core").DesignArtifactManifestDTO} 的 generated[].kind。 */
458
+ type ProjectFileKind = "config" | "workflow" | "views" | "recipe" | "role" | "other";
459
+ /**
460
+ * 禁止 `..`、绝对路径、越出 `rootDir` 的相对路径;非法抛
461
+ * `ApiError{code:"invalid_path"}`(status 400)。
462
+ *
463
+ * WP-10-012:抽出底层校验,供 PG 仓库写 DB 前复用(project.dir / workflow 目录
464
+ * 均以 rootDir 形式传入)。`assertSafeProjectPath` 仍保留 ProjectDTO 入参兼容既有
465
+ * local 仓库调用;`assertSafeWorkflowPath` 以哨兵根做词法越界判定,供 PG 工作流
466
+ * 仓库(无 project.dir)写前校验。
467
+ */
468
+ declare function assertSafeRelativePath(rootDir: string, relPath: string): void;
469
+ /**
470
+ * 禁止 `..`、绝对路径、越出 `project.dir` 的相对路径;非法抛
471
+ * `ApiError{code:"invalid_path"}`(status 400)。
472
+ */
473
+ declare function assertSafeProjectPath(project: ProjectDTO, relPath: string): void;
474
+ /**
475
+ * WP-10-012:工作流内容文件路径安全校验。path 相对工作流目录(`workflows/<dir>/`
476
+ * 或单工作流时的项目根)。PG 仓库层无 project.dir,以哨兵根做词法越界判定
477
+ * (拒 `..`/绝对/越出),保证坏路径不入库;物化层仍会以真实 workflow 目录再做
478
+ * `assertSafeProjectPath`,本层为 DB 写前 defense-in-depth。
479
+ */
480
+ declare function assertSafeWorkflowPath(relPath: string): void;
481
+ /** 判定相对路径是否落入禁止外部写入的目录(runtime/traces/node_modules)。 */
482
+ declare function isForbiddenWritable(relPath: string): boolean;
483
+ /**
484
+ * 校验可写类型:禁止写入 runtime/traces/node_modules,且后缀必须在白名单内;
485
+ * 非法抛 `ApiError{code:"invalid_path"}`。
486
+ */
487
+ declare function assertWritableKind(relPath: string): void;
488
+ /**
489
+ * 文件分类,对齐 DesignArtifactManifestDTO.generated[].kind。
490
+ */
491
+ declare function kindOf(relPath: string): ProjectFileKind;
492
+ /**
493
+ * 对 aitable-workflow.config.yml 内容校验 `scene` 字段存在性;缺失/空返回 warning。
494
+ * 内容非合法 YAML 时返回解析告警;非配置文件返回空数组(由调用方按路径决定是否调用)。
495
+ */
496
+ declare function validateConfigScene(content: string): string[];
497
+ type WriteProjectFilesResult = {
498
+ written: ProjectFileDTO[];
499
+ warnings: string[];
500
+ };
501
+ /**
502
+ * 遍历写入 `project.dir/<path>`,mkdir 递归;返回写入清单与 warnings。
503
+ * 每个文件均经 assertSafeProjectPath + assertWritableKind 校验;配置文件额外提示 scene。
504
+ */
505
+ declare function writeProjectFiles(project: ProjectDTO, files: ProjectFileDTO[]): Promise<WriteProjectFilesResult>;
506
+ /**
507
+ * 删除指定相对路径文件(仍受安全校验,但允许删除 runtime/traces 之外的业务文件)。
508
+ * 删除受保护目录需显式允许——此处仅允许删除业务文件,runtime/traces/node_modules 拒绝。
509
+ */
510
+ declare function removeProjectFiles(project: ProjectDTO, paths: string[]): Promise<void>;
511
+ /**
512
+ * 先删业务文件(config/workflow/views/recipes/roles,保留 runtime/traces/node_modules),
513
+ * 再写新文件。
514
+ */
515
+ declare function replaceProjectFiles(project: ProjectDTO, files: ProjectFileDTO[]): Promise<WriteProjectFilesResult>;
516
+
517
+ declare function mergeTaskIntoState(state: ProjectStateDTO, task: ProjectTaskDTO): ProjectStateDTO;
518
+ declare function normalizeProjectState(project: ProjectDTO, value: Partial<ProjectStateDTO>): ProjectStateDTO;
519
+ declare function isTerminalStatus(status: ProjectTaskDTO["status"] | undefined): boolean;
520
+
521
+ /**
522
+ * WP-10-011:per-project FS 互斥锁。
523
+ *
524
+ * 物化(materializeProjectFiles)/ 同步(syncProjectFilesToDb)/ 文件写
525
+ * (PUT·PATCH /files、workflow files CRUD、create)的「list → 清空 → 写」FS 段经
526
+ * {@link withProjectLock} 串行,避免并发请求交错清写导致磁盘状态不一致或产物丢失。
527
+ *
528
+ * 设计约束(对齐 WP-10-011 #7):
529
+ * - 锁粒度 = 单次 FS 读改写段(materialize / sync / 一次文件写),**不包整个 SSE 流**。
530
+ * design/setup 的 agent 写盘发生在 SSE handler 内部(引擎层,SPEC 不动列表),
531
+ * 本锁不覆盖 agent 写;agent 写与 server 侧 materialize/sync 的交错由 #1「只读入口
532
+ * 不 materialize」+ 「stage active 跳过清空」守卫保护(见 project-materialize.ts)。
533
+ * - DB 段仍由各自 repository 的 writeQueues 串行,本锁只串行 FS 段,二者不嵌套(无死锁)。
534
+ * - 基于 promise 链:同一 projectId 的请求按到达顺序串行;不同 projectId 完全并行。
535
+ * - 非可重入:不要在持锁期间再次对同一 projectId 取锁(会死锁)。当前调用点均为顶层
536
+ * handleRequest / syncStageOnTerminal,不存在嵌套调用,安全。
537
+ */
538
+ type ProjectMutexStore = {
539
+ /**
540
+ * 串行执行 `fn`:同一 `projectId` 的调用按到达顺序排队执行,返回 `fn` 的结果。
541
+ * `fn` 抛错时错误传播给调用方,但**不阻断**后续排队任务(后续任务仍执行)。
542
+ */
543
+ withProjectLock<T>(projectId: string, fn: () => Promise<T>): Promise<T>;
544
+ };
545
+ /**
546
+ * 创建 per-project 互斥锁存储。每个 projectId 维护一条 promise 链;
547
+ * 任务完成后清理链条尾节点,避免长生命周期下 Map 无限增长。
548
+ */
549
+ declare function createProjectMutexStore(): ProjectMutexStore;
550
+
551
+ /**
552
+ * WP-10-011 #1 + #7:stage 活跃判定。
553
+ * design/setup/run 正在跑(SSE 进行中 / RunSession 活跃)时,PG 物化清空会抹掉 stage
554
+ * 进行中的磁盘产物(agent 写盘未回写 DB)。此判定经 prepareWorkdir 透传给 store 实现。
555
+ */
556
+ declare function isStageActiveFor(services: ServerServices, projectId: string): boolean;
557
+ /**
558
+ * stage/写操作入口前置:对齐 project.dir 与权威源。
559
+ * local store:仅 touch 元信息;PG store:per-project 互斥 + stage-active 守卫 + 物化。
560
+ */
561
+ declare function prepareWorkdirGuarded(project: ProjectDTO, services: ServerServices): Promise<MaterializeResult>;
562
+ /**
563
+ * WP-10-010:stage 任务终态后把磁盘产物回写权威源(disk→DB,物化的逆操作)。
564
+ * 非终态(blocked/running/idle)与 reconnect 早返回不触发,避免半成品回写。
565
+ * local store:commitWorkdir no-op。全程 best-effort:失败仅记日志。
566
+ */
567
+ declare function commitStageOnTerminal(services: ServerServices, project: ProjectDTO, stage: StageIdDTO): Promise<void>;
568
+ /**
569
+ * WP-10-001:删除项目后 best-effort 清理存储级联(state/绑定行)与执行绑定。
570
+ * FS 行为不依赖 DB;任何清理失败均吞掉(不阻断已完成的 FS 删除)。
571
+ */
572
+ declare function cascadeDeleteProject(services: ServerServices, projectId: string): Promise<void>;
573
+
574
+ 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, LocalExecutionBackend, LocalIdentityService, LocalProjectStore, type MaterializeOptions, type MaterializeResult, type ProjectFileKind, type ProjectMutexStore, type ProjectRepository, type ProjectStore, type RelayMachineInfo, type RunAppFactory, 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, apiError, assertSafeProjectPath, assertSafeRelativePath, assertSafeWorkflowPath, assertWritableKind, backendOf, buildAITableUrl, cascadeDeleteProject, checksumContent, commitStageOnTerminal, configPath, createHonoApp, createLocalProjectRepository, createLocalWorkflowInstanceStore, createProject, createProjectMutexStore, 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, resolveServeConfig, sendError, sendJson, sendUnauthorized, startServer, touchProject, unauthorized, updateProjectMeta, validateConfigScene, validateDesignArtifacts, warnDeprecatedBootstrapKey, workflowFileKind, writeDesignManifest, writeProjectFiles };
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ var kr=Object.defineProperty;var s=(e,t)=>kr(e,"name",{value:t,configurable:!0});import An from"http";import{Hono as Jo}from"hono";import{detectAgents as Go}from"aitable-workflow-core";async function b(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(b,"readJson");function N(e,t,r=200){e.writeHead(r,{"content-type":"application/json; charset=utf-8"}),e.end(`${JSON.stringify(t)}
2
+ `)}s(N,"sendJson");function H(e,t,r,o,n){N(e,{error:{code:r,message:o,details:n}},t)}s(H,"sendError");function _n(e){e.writeHead(401,{"content-type":"application/json; charset=utf-8"}),e.end(`${JSON.stringify({error:"unauthorized"})}
3
+ `)}s(_n,"sendUnauthorized");function te(e){return{code:E(e),message:T(e),details:W(e)}}s(te,"apiError");function re(e){return typeof e=="object"&&e!==null&&"status"in e&&typeof e.status=="number"?e.status:500}s(re,"errorStatus");function E(e){return typeof e=="object"&&e!==null&&"code"in e&&typeof e.code=="string"?e.code:"INTERNAL_ERROR"}s(E,"errorCode");function T(e){return e instanceof Error?e.message:String(e)}s(T,"errorMessage");function W(e){if(typeof e=="object"&&e!==null&&"details"in e)return e.details}s(W,"errorDetails");import hr from"crypto";function Je(e,t){Tr(e,t),t.setHeader("access-control-allow-methods","GET,POST,PATCH,DELETE,OPTIONS"),t.setHeader("access-control-allow-headers","content-type, authorization")}s(Je,"setCors");function Tr(e,t){let r=vr();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(Tr,"applyCorsOrigin");function vr(){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(vr,"readAllowedOrigins");function jr(){let e=process.env.AITABLE_WORKFLOW_API_TOKEN,t=typeof e=="string"?e.trim():"";return t||void 0}s(jr,"readServerApiToken");function Ge(e){let t=jr();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?Pr(i,t):!1}s(Ge,"isRequestAuthorized");function Pr(e,t){let r=Buffer.from(e),o=Buffer.from(t);return r.length!==o.length?!1:hr.timingSafeEqual(r,o)}s(Pr,"timingSafeEqualString");function Ke(e){return Array.isArray(e)?e[0]:e}s(Ke,"headerValue");function Ve(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(Ve,"isAuthExempt");function y(e,t,r=200){return e.newResponse(`${JSON.stringify(t)}
4
+ `,r,{"content-type":"application/json; charset=utf-8"})}s(y,"jsonBody");function _(e,t,r,o,n){return y(e,{error:{code:r,message:o,details:n}},t)}s(_,"errorBody");function Ye(e){return e.newResponse(`${JSON.stringify({error:"unauthorized"})}
5
+ `,401,{"content-type":"application/json; charset=utf-8"})}s(Ye,"unauthorized");function C(e){return async t=>(await e(t),t.env.rawHandled=!0,t.body(null))}s(C,"raw");function G(e){return e.env.services.execution}s(G,"backendOf");async function Ie(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(Ie,"resolveProjectMw");import{Hono as Uo}from"hono";import le from"path";import{validateDesignOutput as qo,buildEnvSource as zo,loadWorkflowTemplate as Zt}from"aitable-workflow-core";import{logger as Dr}from"aitable-workflow-core";var oe=new Set,ue=new Map;function Re(e,t,r){e.write(`id: ${t}
6
+ `),e.write(`event: ${r.type}
7
+ `),e.write(`data: ${JSON.stringify(r)}
8
+
9
+ `)}s(Re,"writeDesignSse");function Qe(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{Re(o,r.seq,t)}catch{e.subscribers.delete(o)}}}s(Qe,"publishDesignEvent");function Ze(e){oe.delete(e);let t=ue.get(e);if(ue.delete(e),!!t){for(let r of t.subscribers)if(!r.writableEnded)try{r.end()}catch{}t.subscribers.clear()}}s(Ze,"closeDesignSession");function Xe(e,t){let r=e.searchParams.get("cursor")??Ke(t.headers["last-event-id"]);if(r==null||r==="")return;let o=Number(r);return Number.isFinite(o)?o:void 0}s(Xe,"parseReconnectCursor");function Sr(e,t){return e.runtime.runSessions.has(t)||oe.has(t)}s(Sr,"isStageActiveFor");function v(e,t){return t.projectStore.prepareWorkdir(e,{isStageActive:s(()=>Sr(t,e.id),"isStageActive")})}s(v,"prepareWorkdirGuarded");async function pe(e,t,r){try{let o=await e.projectStore.instances.getState(t),n=o.lastTasks[r];if(!n)return;let i=n.status;if(i!=="succeeded"&&i!=="failed"&&i!=="cancelled")return;await e.projectStore.commitWorkdir(t,{stage:o.stage})}catch(o){Dr.warn("stage-sync","stage \u6D41\u56DE\u5199\u6743\u5A01\u6E90\u5931\u8D25",{projectId:t.id,stage:r,error:T(o)})}}s(pe,"commitStageOnTerminal");async function et(e,t){await e.projectStore.cascadeDelete(t).catch(()=>{}),await e.execution.unbindProjectMachine(t).catch(()=>{})}s(et,"cascadeDeleteProject");import Ce from"path";import{readFile as $r,writeFile as Hr,access as Ur,readdir as qr}from"fs/promises";import St from"yaml";import{scaffold as zr,writeRoleDefinitions as Jr,isRecord as Ot}from"aitable-workflow-core";import{logger as gt}from"aitable-workflow-core";import{createHash as Or,randomUUID as br}from"crypto";import{mkdir as xe,readdir as tt,readFile as Ee,rm as rt,stat as ot,writeFile as nt}from"fs/promises";import P from"path";var Ar=P.resolve(process.cwd(),".sandbox","aitable-workflow-server"),st=/^[a-zA-Z0-9_-]+$/,Ir=/\.(json|ya?ml|ts|js|md|txt)$/i,Rr=["workflow.yml","views.yml","recipes","traces"];function it(e={}){let t=P.resolve(e.dataRoot??process.env.AITABLE_WORKFLOW_DATA_DIR??Ar);return{dataRoot:t,projectsRoot:P.join(t,"projects")}}s(it,"createWorkspace");async function at(e){await xe(e.projectsRoot,{recursive:!0});let t=new Date().toISOString(),r=(await _e(e)).length+1,o={id:`wf_${br().slice(0,8)}`,title:`\u5DE5\u4F5C\u6D41 #${r}`,dir:"",createdAt:t,updatedAt:t};return o.dir=lt(e,o.id),await xe(o.dir,{recursive:!0}),await we(o),o}s(at,"createProject");async function ne(e,t){let r=await A({dataRoot:P.dirname(P.dirname(e.dir)),projectsRoot:P.dirname(e.dir)},e.id),o={...r,...t,title:t.title?.trim()||r.title,updatedAt:new Date().toISOString()};return await we(o),o}s(ne,"updateProjectMeta");async function _e(e){let t=await tt(e.projectsRoot,{withFileTypes:!0}).catch(()=>[]);return(await Promise.all(t.filter(o=>o.isDirectory()&&st.test(o.name)).map(o=>A(e,o.name).catch(()=>null)))).filter(o=>o!==null).sort((o,n)=>n.updatedAt.localeCompare(o.updatedAt))}s(_e,"listProjects");async function A(e,t){ft(t);let r=lt(e,t);if(!(await ot(r).catch(()=>null))?.isDirectory())throw Object.assign(new Error(`\u9879\u76EE\u4E0D\u5B58\u5728: ${t}`),{code:"PROJECT_NOT_FOUND",status:404});let n=P.join(r,"project.json"),i=await Ee(n,"utf8").catch(()=>"");if(!i){let c=new Date().toISOString(),u={id:t,dir:r,createdAt:c,updatedAt:c};return await we(u),u}return{...JSON.parse(i),dir:r}}s(A,"resolveProject");async function I(e){let t={...e,updatedAt:new Date().toISOString()};return await we(t),t}s(I,"touchProject");async function ct(e){return await Promise.all(Rr.map(t=>rt(P.join(e.dir,t),{recursive:!0,force:!0}))),await I(e)}s(ct,"resetProjectWorkflow");async function ge(e){await rt(e.dir,{recursive:!0,force:!0})}s(ge,"deleteProject");async function xr(e){let t=await Ee(dt(e),"utf8").catch(()=>"");return t.trim()?JSON.parse(t):null}s(xr,"readDesignManifest");async function Fe(e,t){await xe(pt(e),{recursive:!0}),await nt(dt(e),`${JSON.stringify(t,null,2)}
10
+ `,"utf8")}s(Fe,"writeDesignManifest");async function ut(e){let t=await xr(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 ot(P.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(ut,"validateDesignArtifacts");function de(e){return Or("sha256").update(e).digest("hex")}s(de,"checksumContent");async function S(e){let t=[];return await mt(e.dir,e.dir,t),t.sort((r,o)=>r.path.localeCompare(o.path))}s(S,"listProjectFiles");function F(e){return P.join(e.dir,"aitable-workflow.config.yml")}s(F,"configPath");function We(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(We,"buildAITableUrl");function Er(e){return P.join(e.dir,"runtime")}s(Er,"runtimeDir");function pt(e){return P.join(Er(e),"artifacts")}s(pt,"artifactsDir");function dt(e){return P.join(pt(e),"design-manifest.json")}s(dt,"designManifestPath");function lt(e,t){return ft(t),P.join(e.projectsRoot,t)}s(lt,"projectDir");function ft(e){if(!st.test(e))throw Object.assign(new Error(`\u975E\u6CD5\u9879\u76EE ID: ${e}`),{code:"INVALID_PROJECT_ID"})}s(ft,"assertProjectId");async function we(e){await nt(P.join(e.dir,"project.json"),`${JSON.stringify(e,null,2)}
11
+ `,"utf8")}s(we,"writeProjectMeta");async function mt(e,t,r){let o=await tt(t,{withFileTypes:!0}).catch(()=>[]);for(let n of o){if(n.name==="node_modules"||n.name==="traces"||n.name==="runtime")continue;let i=P.join(t,n.name);if(n.isDirectory()){await mt(e,i,r);continue}if(!n.isFile()||!Ir.test(n.name))continue;let a=P.relative(e,i).split(P.sep).join("/"),c=await Ee(i,"utf8").catch(()=>"");r.push({path:a,content:c})}}s(mt,"walk");function yt(e){return{appFactory:e,runSessions:new Map}}s(yt,"createServerRuntime");async function kt(e,t,r,o,n,i){let a=await A(t,e),c=await r.getState(a),u=o.runSessions.get(e);if(u){wt(u,n,i);let m=await r.getState(a),d=m.activeTask?.stage==="run"?m.activeTask:m.lastTasks.run;d&&Tt(u,{type:"task.snapshot",task:d},i);return}if(c.activeTask?.stage==="run"&&c.activeTask.status==="running"){let d={type:"task.failed",task:await r.updateTask(a,c.activeTask.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,d.type,d),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 p=await r.createTask(a,"run"),l={taskId:p.id,subscribers:new Set,events:[],app:null,snapshotWrite:Promise.resolve()};o.runSessions.set(e,l),wt(l,n,i),_r(a,p,l,o,r,i)}s(kt,"handleRunRuntime");function ht(e,t){if(!t)return e;let r=e.lastTasks.run;return r?.status==="running"?e:{...e,stage:"run",activeTask:r??e.activeTask,stageRuntime:{...e.stageRuntime,run:{status:"running",taskId:t.taskId,checkpoint:"listening",message:"\u771F\u5B9E\u8FD0\u884C\u670D\u52A1\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u76D1\u542C AI \u8868\u683C\u4E2D\u7684\u5F85\u5904\u7406\u8BB0\u5F55",retryable:!1}}}}s(ht,"overlayRunSessionState");async function _r(e,t,r,o,n,i){let a=s(u=>{Tt(r,u,i),Wr(u)&&(gt.info("run-runtime","publish run execution event",Cr(u)),r.snapshotWrite=r.snapshotWrite.then(()=>n.appendRunSnapshotEvent(e,u)).catch(p=>{gt.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(F(e),l=>a(Fr(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:vt(u),message:jt(u),retryable:!0,details:Pt(u)}});a({type:"task.failed",task:p}),a({type:"error",error:Nr(u)})}finally{o.runSessions.delete(e.id),Mr(r)}}s(_r,"startRunSession");function Fr(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(Fr,"toRunEventDTO");function Wr(e){return e.type.startsWith("run.record.")||e.type.startsWith("run.step.")}s(Wr,"isRunExecutionEvent");function Cr(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(Cr,"summarizeRunExecutionEvent");function wt(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(wt,"attachRunSubscriber");function Tt(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(Tt,"publishRunEvent");function Mr(e){for(let t of e.subscribers)t.end();e.subscribers.clear()}s(Mr,"closeRunSubscribers");function Nr(e){return{code:vt(e),message:jt(e),details:Pt(e)}}s(Nr,"apiError");function vt(e){return typeof e=="object"&&e!==null&&"code"in e&&typeof e.code=="string"?e.code:"INTERNAL_ERROR"}s(vt,"errorCode");function jt(e){return e instanceof Error?e.message:String(e)}s(jt,"errorMessage");function Pt(e){if(typeof e=="object"&&e!==null&&"details"in e)return e.details}s(Pt,"errorDetails");function Dt(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(Dt,"deriveProjectTitleFromSop");function Lr(){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(Lr,"readSseHeartbeatMs");function L(e){e.writeHead(200,{"content-type":"text/event-stream; charset=utf-8","cache-control":"no-cache, no-transform",connection:"keep-alive"}),Br(e)}s(L,"startSse");function Br(e){let t=Lr(),r=setInterval(()=>{if(e.writableEnded||!e.writable){clearInterval(r);return}try{e.write(`:keepalive
12
+
13
+ `)}catch{clearInterval(r)}},t);r.unref?.();let o=s(()=>clearInterval(r),"stop");e.on("close",o),e.on("finish",o)}s(Br,"startSseHeartbeat");function U(e,t,r){e.write(`event: ${t}
14
+ `),e.write(`data: ${JSON.stringify(r)}
15
+
16
+ `)}s(U,"writeSse");async function bt(e,t,r,o,n,i){let a=await A(t,e),c=await b(o),u=await r.createTask(a,"init");try{let p=Kr(c),l=await zr({answers:p,cwd:a.dir,force:!0}),m=await Jr(Ce.join(a.dir,"roles"),c.roles??[]);c.machineId&&i&&await i(e,c.machineId).catch(()=>{});let d=await I(a),k=m.skippedKnowledgeBindings.map(q=>`\u89D2\u8272 ${q.roleId} \u7684\u77E5\u8BC6\u5E93\u7ED1\u5B9A\u672A\u5199\u5165 role.yml\uFF0C\u540E\u7EED\u5E94\u4E0B\u6C89\u5230\u90E8\u7F72 Binding\uFF1A${q.knowledge.join(", ")}`);await r.updateTask(a,u.id,{status:"succeeded",checkpoint:"done",currentAction:"\u914D\u7F6E\u5DF2\u5B8C\u6210",outputs:{warnings:k,roleFiles:m.files}});let w=Dt(c.sop,1),j=await ne(d,{title:w});N(n,{project:j,scaffold:l,roleFiles:m.files,files:await S(d),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:E(p),message:T(p),retryable:!0,details:W(p)}}),p}}s(bt,"handleInit");async function At(e,t,r,o,n){let i=await A(t,e),c=(await b(o)).tableId?.trim()??"";if(!c)throw Object.assign(new Error("Table Id \u4E0D\u80FD\u4E3A\u7A7A"),{code:"TABLE_ID_REQUIRED",status:422});let u=F(i),p=await $r(u,"utf8"),l=St.parse(p);if(!Ot(l)||!Ot(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 Hr(u,St.stringify(l),"utf8");let m=await I(i);N(n,{project:m,files:await S(m),state:await r.getRecoverableState(m)})}s(At,"handleUpdateProjectTableId");async function It(e,t,r,o,n,i,a){let c=await A(t,e),u=await b(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(w=>w==="\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 m=u.action==="confirm"?"\u4EBA\u5DE5\u786E\u8BA4\uFF1A\u7EE7\u7EED\u6D41\u7A0B":"\u4EBA\u5DE5\u786E\u8BA4\uFF1A\u7EC8\u6B62\u6D41\u7A0B",d=null;u.action==="confirm"&&(d=await Gr(p,n)),await p.tracker.updateState(n,l),d&&await p.tracker.updateCurrentStep(n,d),await p.tracker.appendEvent(n,m);let k=await r.updateRunSnapshotRecord(c,n,u.action==="confirm"?{state:l,status:"running",currentStepId:d??void 0}:{state:l,status:"done",currentStepId:null},m);N(a,{action:u.action,recordId:n,state:k})}s(It,"handleRunHumanAction");async function Gr(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(Gr,"resolveNextStepId");async function Me(e,t,r,o,n,i){L(i),await kt(e,t,r,o,i,U)}s(Me,"handleRun");function Kr(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:Yr(e.agentsSelected),scene:Vr(e),baseId:t,tableId:e.tableId?.trim()??"",llmProvider:r,llmModel:r?e.llmModel.trim()||Qr(r):"",llmApiKeyEnv:r?Zr(r):"",confirmed:!0}}s(Kr,"toInitAnswers");function Vr(e){let t=e.roles.map(r=>r.name).filter(Boolean).join("\u3001");return["aitable-workflow \u4E1A\u52A1\u81EA\u52A8\u5316\u573A\u666F\u3002",e.sop.trim()?`\u7528\u6237 SOP\uFF1A
17
+ ${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",t?`\u53C2\u4E0E\u89D2\u8272\uFF1A${t}`:""].filter(Boolean).join(`
18
+
19
+ `)}s(Vr,"buildScene");function Yr(e){return e.includes("qoder")?"qoder":e.includes("codex")?"codex":"claudecode"}s(Yr,"toAgentType");function Qr(e){return e==="openai"?"gpt-4o":e==="anthropic"?"claude-sonnet-4-20250514":e==="dashscope"?"qwen-max":"Qwen3.7-Max-DogFooding"}s(Qr,"defaultModel");function Zr(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(Zr,"defaultApiKeyEnv");async function Rt(e,t){let r=Ce.join(e.dir,"aitable-workflow.config.yml");try{await Ur(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")}let o=Ce.join(e.dir,"roles");try{(await qr(o).catch(()=>[])).some(a=>a.endsWith(".role.yml")||a.endsWith(".role.yaml"))||t.warnings.push("roles \u76EE\u5F55\u65E0 .role.yml \u89D2\u8272\u6587\u4EF6\uFF08design \u9636\u6BB5 recipe \u9700\u7ED1\u5B9A role\uFF09")}catch{t.warnings.push("roles \u76EE\u5F55\u4E0D\u5B58\u5728\uFF08design \u9636\u6BB5 recipe \u9700\u7ED1\u5B9A role\uFF09")}}s(Rt,"augmentProjectFileValidation");import xt from"fs";import Xr from"path";var eo=/requiresAgent\s*:\s*(true|false)/,Et=new Map;function _t(e,t){if(!t)return!1;for(let r of[".recipe.ts",".recipe.js"]){let o=Xr.join(e,`${t}${r}`);try{let n=xt.statSync(o);if(!n.isFile())continue;let i=`${n.mtimeMs}:${n.size}`,a=Et.get(o);if(a?.signature===i)return a.requiresAgent;let u=xt.readFileSync(o,"utf8").match(eo),p=u?u[1]==="true":!1;return Et.set(o,{signature:i,requiresAgent:p}),p}catch(n){if(n.code==="ENOENT")continue;return!1}}return!1}s(_t,"readRecipeRequiresAgent");import{rm as Wt,mkdir as to,writeFile as ro}from"fs/promises";import R from"path";import oo from"yaml";import{isRecord as no}from"aitable-workflow-core";var Ft=/\.(json|ya?ml|ts|js|md|txt)$/i,so=new Set(["runtime","traces","node_modules"]),Ct="aitable-workflow.config.yml";function Mt(e,t){if(typeof t!="string"||t.trim()==="")throw Object.assign(new Error("\u6587\u4EF6\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A"),{code:"invalid_path",status:400});if(R.isAbsolute(t))throw Object.assign(new Error(`\u4E0D\u5141\u8BB8\u7EDD\u5BF9\u8DEF\u5F84: ${t}`),{code:"invalid_path",status:400});let r=R.normalize(t).split(R.sep).join("/");if(r===".."||r.startsWith("../")||r.includes("/../")||r.startsWith("..\\"))throw Object.assign(new Error(`\u8DEF\u5F84\u4E0D\u5141\u8BB8\u5305\u542B ..: ${t}`),{code:"invalid_path",status:400});let o=R.resolve(e,r),n=R.relative(e,o);if(n.startsWith("..")||R.isAbsolute(n))throw Object.assign(new Error(`\u8DEF\u5F84\u8D8A\u51FA\u9879\u76EE\u76EE\u5F55: ${t}`),{code:"invalid_path",status:400})}s(Mt,"assertSafeRelativePath");function Nt(e,t){Mt(e.dir,t)}s(Nt,"assertSafeProjectPath");function As(e){Mt("/workflow-root",e)}s(As,"assertSafeWorkflowPath");function Lt(e){let r=R.normalize(e).split(R.sep).join("/").split("/")[0]??"";return so.has(r)}s(Lt,"isForbiddenWritable");function io(e){if(Lt(e))throw Object.assign(new Error(`\u7981\u6B62\u5199\u5165\u53D7\u4FDD\u62A4\u76EE\u5F55: ${e}`),{code:"invalid_path",status:400});if(!Ft.test(e))throw Object.assign(new Error(`\u6587\u4EF6\u7C7B\u578B\u4E0D\u5141\u8BB8\u5199\u5165: ${e}\uFF08\u4EC5\u5141\u8BB8 ${Ft.source}\uFF09`),{code:"invalid_path",status:400})}s(io,"assertWritableKind");function Bt(e){let t=R.normalize(e).split(R.sep).join("/");return t===Ct||t.endsWith(".config.yml")?"config":t==="workflow.yml"?"workflow":t==="views.yml"?"views":t.startsWith("recipes/")?"recipe":t.startsWith("roles/")?"role":"other"}s(Bt,"kindOf");function ao(e){let t=[],r;try{r=oo.parse(e)}catch(n){return t.push(`aitable-workflow.config.yml \u89E3\u6790\u5931\u8D25: ${n instanceof Error?n.message:String(n)}`),t}if(!no(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(ao,"validateConfigScene");async function ye(e,t){let r=[],o=[];for(let n of t){Nt(e,n.path),io(n.path);let i=R.join(e.dir,n.path);await to(R.dirname(i),{recursive:!0}),await ro(i,n.content,"utf8"),r.push({path:n.path,content:n.content}),n.path===Ct&&o.push(...ao(n.content))}return{written:r,warnings:o}}s(ye,"writeProjectFiles");async function $t(e,t){for(let r of t){if(Nt(e,r),Lt(r))throw Object.assign(new Error(`\u7981\u6B62\u5220\u9664\u53D7\u4FDD\u62A4\u76EE\u5F55: ${r}`),{code:"invalid_path",status:400});await Wt(R.join(e.dir,r),{force:!0})}}s($t,"removeProjectFiles");async function Ht(e,t){let r=await co(e);for(let o of r)await Wt(R.join(e.dir,o),{force:!0}).catch(()=>{});return ye(e,t)}s(Ht,"replaceProjectFiles");async function co(e){return(await S(e)).map(r=>r.path).filter(r=>Bt(r)!=="other")}s(co,"collectBusinessFiles");function uo(e){return de(e.content)}s(uo,"checksumFile");async function Ut(e){let t=await S(e),r=t.map(o=>({path:o.path,sha256:uo(o)}));return{project:e,files:t,checksums:r,exportedAt:new Date().toISOString()}}s(Ut,"buildProjectExport");var po="coding-agents.config.json";function qt(e){let t=[],r=e.files,o=e.checksums,n=r.findIndex(i=>i.path===po);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 m=u[l];if(!m||typeof m!="object"||Array.isArray(m))continue;let d=m.env;if(d&&typeof d=="object"&&!Array.isArray(d)){let k=d;if(Object.values(k).some(j=>typeof j=="string"&&j!=="")){for(let j of Object.keys(k))k[j]="";p=!0}}}p&&(a=JSON.stringify(c,null,2)+`
20
+ `)}}}catch{}if(a!==i.content){let c={path:i.path,content:a};r=r.map((p,l)=>l===n?c:p);let u=de(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(qt,"redactExportBundle");function zt(e){let t=s(r=>r.env.services.projectStore.workflows?null:_(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 o=t(r);if(o)return o;let{services:n,scope:i}=r.env;return y(r,{workflows:await n.projectStore.workflows.list(r.env.project.id,i)})}),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 b(a);if(!u.name||!u.name.trim())return _(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 v(c,n);let l={workflow:p.workflow};return p.files&&(l.files=p.files),p.warnings&&p.warnings.length>0&&(l.warnings=p.warnings),y(r,l)}),e.get("/:projectId/workflows/:workflowId",async r=>{let o=t(r);if(o)return o;let{services:n,scope:i}=r.env,a=await n.projectStore.workflows.exportWorkflow(r.env.project.id,r.req.param("workflowId")??"",i);return y(r,{workflow:a.workflow,files:a.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 _(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 v(a,n),y(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 b(a);if(!Array.isArray(p.files))return _(r,422,"INVALID_INPUT","files \u4E0D\u80FD\u4E3A\u7A7A");let l=await n.projectStore.workflows.replaceFiles(u,p.files,i);return await v(c,n),y(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 b(a),l=await n.projectStore.workflows.updateFiles(u,{upsert:p.upsert,remove:p.remove},i);return await v(c,n),y(r,{workflow:u,files:l})})}s(zt,"registerWorkflowRoutes");import lo from"path";import{collectDesignContext as Le,runDesign as fo,syncFieldsFromWorkflow as mo,syncViewsFromDesign as go,runAutoDiscovery as wo,buildDesignBrief as yo,logger as ko}from"aitable-workflow-core";var ke=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 Gt(e,t,r,o){let n=await A(t,e),i=await r.getState(n),a=i.lastTasks.design?.status==="failed"?i.lastTasks.design:null;if(a){await r.clearActiveTask(n,a.id),N(o,{task:a,error:{code:"DESIGN_RETRY_REQUIRED",message:a.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 c=i.activeTask?.stage==="design"?i.activeTask:null,u=c&&(c.status==="running"||c.status==="blocked")?c:await r.createTask(n,"design");try{let p=F(n),l=await Le({configPath:p,yes:!0}),m=await wo({scene:l.scene,configPath:p,mode:"fast"}),d={form:m.form,answers:m.answers,summary:m.summary,confirmed:!1},k=await r.updateTask(n,u.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:{...u.outputs,discovery:d}});N(o,{task:k,discovery:d})}catch(p){let l=await r.updateTask(n,u.id,{status:"failed",checkpoint:"discovery_precheck",currentAction:"\u9700\u6C42\u6F84\u6E05\u51C6\u5907\u5931\u8D25",error:{code:"DESIGN_PRECHECK_FAILED",message:T(p),retryable:!0,details:W(p)}});N(o,{task:l,error:te(p)},500)}}s(Gt,"handleDesignDiscovery");async function Kt(e,t,r,o,n){let i=await A(t,e),a=await r.getState(i),c=a.activeTask?.stage==="design"?a.activeTask:a.lastTasks.design,u=c?.outputs?.discovery;if(!c||!u){H(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 p=o.answers??u.answers,l=F(i),m=await Le({configPath:l,yes:!0}),d=await yo({scene:m.scene,filled:[{form:u.form,answers:p}]}),k={...u,answers:p,clarifications:d,summary:d.replace(/\s+/g," ").trim(),confirmed:!0},w=await r.updateTask(i,c.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:{...c.outputs,discovery:k,clarifications:d}});N(n,{task:w,discovery:k})}s(Kt,"handleDesignDiscoveryConfirm");async function Vt(e,t,r,o,n,i){let a=await A(t,e);try{n.ensureAvailable()}catch(f){H(o,re(f),E(f),T(f));return}let c=await r.getState(a),u=c.activeTask?.stage==="design"?c.activeTask:null;if(u&&u.status==="blocked"){L(o),U(o,"task.blocked",{type:"task.blocked",task:u}),o.end();return}if(u&&u.status==="running"&&u.checkpoint!=="discovery_confirmed"){L(o),U(o,"task.snapshot",{type:"task.snapshot",task:u}),o.end();return}if(oe.has(a.id)){L(o);let f=ue.get(a.id);if(f){for(let h of f.buffer.replayAfter(i))Re(o,h.seq,h.event);f.subscribers.add(o),o.on("close",()=>f.subscribers.delete(o));return}u&&U(o,"task.snapshot",{type:"task.snapshot",task:u}),o.end();return}let p=c.lastTasks.design,l=p?.outputs?.discovery,m=p?.status==="failed"&&l?.confirmed&&typeof p.outputs?.clarifications=="string",d=u?.checkpoint==="discovery_confirmed"?u:m?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:l,clarifications:p.outputs?.clarifications}}):await r.createTask(a,"design");oe.add(a.id),L(o);let k={buffer:new ke,subscribers:new Set([o])};ue.set(a.id,k),o.on("close",()=>k.subscribers.delete(o));let w=s(f=>Qe(k,f),"send"),j=Date.now(),q=j,ie=0,V=[],z=[],O=s((f,h,D)=>{let x=Date.now(),X=x-j,ee=x-q;q=x,V.push({phase:f,message:h,elapsedMs:X,deltaMs:ee,detail:D}),ko.info("design-runtime",h,{projectId:a.id,taskId:d.id,phase:f,elapsedMs:X,deltaMs:ee,...D?{detail:D}:{}}),w({type:"design.progress",phase:f,message:h,elapsedMs:X,deltaMs:ee,detail:D})},"emitProgress"),M=s(async(f,h,D)=>{O(f,h,D);let x=await r.updateTask(a,d.id,{currentAction:h,checkpoint:f});w({type:"task.progress",taskId:x.id,message:h,checkpoint:f})},"progress"),me="",ae=!1,ce=!1,Z=s(async()=>{if(ae){ce=!0;return}ae=!0;try{do{ce=!1;let h=(await S(a)).filter(D=>D.path!=="project.json").map(D=>`${D.path}:${D.content}`).join("|");if(h&&h!==me){me=h;let D=await I(a),x=await S(D);O("file_snapshot",`\u540C\u6B65 ${x.length} \u4E2A\u751F\u6210\u6587\u4EF6`,{fileCount:x.length}),w({type:"design.files",files:x})}}while(ce)}finally{ae=!1}},"sendFileSnapshot");try{w({type:"task.started",task:d}),w({type:"design.started",projectId:e}),await Z();let f,h;try{await M("precheck","\u8BFB\u53D6 Design \u914D\u7F6E"),h=F(a),f=await Le({configPath:h,yes:!0}),await M("precheck_done","Design \u914D\u7F6E\u8BFB\u53D6\u5B8C\u6210",{agent:f.agentType,timeoutMs:f.agentTimeoutMs})}catch(g){let J=await r.updateTask(a,d.id,{status:"failed",checkpoint:"precheck",currentAction:"Design \u9884\u68C0\u67E5\u5931\u8D25",error:{code:"DESIGN_PRECHECK_FAILED",message:T(g),retryable:!0,details:W(g)}});w({type:"task.failed",task:J}),w({type:"error",error:te(g)}),o.end();return}let D=d.outputs?.discovery,x=typeof d.outputs?.clarifications=="string"?d.outputs.clarifications:D?.clarifications;if(!D?.confirmed||!x){await M("waiting_input","\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u9700\u6C42\u6F84\u6E05");let g=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});w({type:"task.blocked",task:g}),o.end();return}f.clarifications=x,await M("brief_ready","\u9700\u6C42\u6458\u8981\u5DF2\u786E\u8BA4\uFF0C\u51C6\u5907\u542F\u52A8 Agent"),w({type:"design.discovery",summary:D.summary});let X=f.agentTimeoutMs;await M("agent_start","\u542F\u52A8 Coding Agent \u751F\u6210\u6D41\u7A0B\u65B9\u6848",{agent:f.agentType,timeoutMs:X,timeoutSource:"config",timeoutConfig:"agent.timeout_ms"});let ee=!1,qe=await n.buildDesignExecutor({projectId:a.id,agent:f.agentType,projectDir:a.dir}),$=await fo(f,{...qe?{executor:qe}:{},timeoutMs:X,onRoundStart:s((g,J)=>{O("agent_round",`\u7B2C ${g} \u8F6E ${J?"\u4FEE\u6B63":"\u751F\u6210"}\u5F00\u59CB`,{round:g,isFix:J}),w({type:"design.round_start",round:g,isFix:J})},"onRoundStart"),onEvent:s(g=>{z.push(ho(g)),z.length>30&&z.shift(),ee||(ee=!0,O("agent_first_event",`\u6536\u5230 Agent \u9996\u4E2A\u4E8B\u4EF6\uFF1A${g.type}`,{eventType:g.type})),g.type==="tool_use"&&O("agent_tool","Agent \u8C03\u7528\u5DE5\u5177",{toolName:g.toolName}),(g.type==="text"||g.type==="thinking")&&Date.now()-ie>5e3&&(ie=Date.now(),O("agent_output",g.type==="thinking"?"Agent \u6B63\u5728\u63A8\u7406":"Agent \u8F93\u51FA\u5185\u5BB9",{eventType:g.type,preview:g.content?.replace(/\s+/g," ").slice(0,120)})),w({type:"design.agent_event",event:g}),(g.type==="tool_use"||g.type==="tool_result")&&Z()},"onEvent"),onAgentLifecycle:s(g=>{O(To(g.phase),vo(g),jo(g))},"onAgentLifecycle"),onValidation:s((g,J)=>{O("validating",g.valid?"\u6D41\u7A0B\u65B9\u6848\u6821\u9A8C\u901A\u8FC7":`\u6D41\u7A0B\u65B9\u6848\u6821\u9A8C\u53D1\u73B0 ${g.errors.length} \u4E2A\u95EE\u9898`,{round:J,valid:g.valid,errorCount:g.errors.length}),w({type:"design.validation",round:J,validation:g}),Z()},"onValidation")});if(!$.success){await Z();let g=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:$.response,retryable:!0},outputs:{...d.outputs,diagnostics:Ne(j,V,z,$.validation)}});await Jt(a,g,$.response),w({type:"task.failed",task:g}),w({type:"design.done",success:!1,rounds:$.rounds,response:$.response}),o.end();return}await M("sync_fields","\u540C\u6B65\u5B57\u6BB5\u8BA1\u5212\u5230\u914D\u7F6E"),await mo({workflowPath:f.workflowPath,configPath:h,autoUpdate:!0}),await M("sync_views","\u540C\u6B65\u89C6\u56FE\u8BA1\u5212\u5230\u914D\u7F6E"),await go({viewsPath:lo.resolve(f.workDir,"views.yml"),configPath:h,autoUpdate:!0}).catch(()=>null),await Z(),await M("manifest","\u5199\u5165 Design \u4EA7\u7269\u6E05\u5355");let wr=await S(a),ze=Po(a,d,wr);await Fe(a,ze);let yr=await r.updateTask(a,d.id,{status:"succeeded",currentAction:"\u5DE5\u4F5C\u6D41\u8BBE\u8BA1\u5B8C\u6210",checkpoint:"done",outputs:{...d.outputs,manifest:ze,diagnostics:Ne(j,V,z,$.validation)}});w({type:"task.succeeded",task:yr}),w({type:"design.done",success:!0,rounds:$.rounds,response:$.response}),o.end()}catch(f){await Z().catch(()=>{});let h=await r.updateTask(a,d.id,{status:"failed",checkpoint:"generating",currentAction:"\u771F\u5B9E\u8BBE\u8BA1\u751F\u6210\u5931\u8D25",error:{code:E(f)==="INTERNAL_ERROR"?"DESIGN_GENERATION_FAILED":E(f),message:T(f),retryable:!0,details:W(f)},outputs:{...d.outputs,diagnostics:Ne(j,V,z)}});await Jt(a,h,T(f)),w({type:"task.failed",task:h}),w({type:"error",error:te(f)}),w({type:"design.done",success:!1,rounds:0,response:T(f)}),o.end()}finally{Ze(a.id)}}s(Vt,"handleDesign");function ho(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(ho,"summarizeAgentEvent");function To(e){return`agent_${e}`}s(To,"agentLifecyclePhase");function vo(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(vo,"agentLifecycleMessage");function jo(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(jo,"sanitizeAgentLifecycle");function Ne(e,t,r,o){return{elapsedMs:Date.now()-e,timeline:t.slice(-40),recentAgentEvents:r,validation:o}}s(Ne,"buildDesignDiagnostics");function Po(e,t,r){let o=r.filter(a=>a.path==="workflow.yml"||a.path==="views.yml"||a.path.startsWith("recipes/")).map(a=>({path:a.path,kind:a.path==="workflow.yml"?"workflow":a.path==="views.yml"?"views":"recipe",checksum:de(a.content),required:a.path==="workflow.yml"||a.path.startsWith("recipes/")})),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(Po,"buildDesignManifest");async function Jt(e,t,r){let o=new Date().toISOString();await Fe(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(Jt,"writeInvalidDesignManifest");import xo from"path";import{loadSettings as Eo,resolveConfigPath as _o,loadWorkflowTemplate as Fo,initTable as Wo,ensureTableExists as Co,syncViewsFromDesign as Mo,runWithExecutor as No,runWithDwsRunner as Lo,DwsError as Bo,logger as $o}from"aitable-workflow-core";var Do={singleSelect:"single_select",multipleSelect:"multiple_select",date:"datetime"},So={Grid:"grid",FormDesigner:"form",Kanban:"kanban",Calendar:"calendar",Gantt:"gantt"};function Yt(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 m=o.get(c);r.set(c,{id:Io(c),name:c,type:Ao(m?.type??p),...m?.options??l?{options:m?.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",bo(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=Oo(e.tracker.views??[],t).map(c=>({id:c.id,name:c.name,kind:So[c.type]??"grid",desc:c.description??Ro(c)})),a=[...r.values()];return{fieldCount:a.length,viewCount:i.length,fields:a,views:i}}s(Yt,"buildSetupSchemaSummary");function Oo(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(Oo,"expandViews");function bo(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(bo,"stateOptions");function Ao(e){return Do[e]??e}s(Ao,"displayFieldType");function Io(e){return`fld_${e.replace(/[^\p{L}\p{N}]+/gu,"_").replace(/^_+|_+$/g,"")||"field"}`}s(Io,"fieldId");function Ro(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(Ro,"viewDescription");async function Be(e,t,r,o,n){let i=await A(t,e);try{n.ensureAvailable()}catch(d){H(o,re(d),E(d),T(d));return}let a=await r.getState(i);if(a.activeTask?.stage==="setup"&&a.activeTask.status==="running"){L(o),U(o,"task.snapshot",{type:"task.snapshot",task:a.activeTask}),o.end();return}let c=a.activeTask?.stage==="setup"&&a.activeTask.status==="blocked"?await r.updateTask(i,a.activeTask.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");L(o);let u=s(d=>U(o,d.type,d),"send"),p=s(async(d,k)=>{let w=await r.updateTask(i,c.id,{currentAction:d,checkpoint:k});u({type:"task.progress",taskId:w.id,message:d,checkpoint:k}),u({type:"setup.progress",message:d})},"progress"),l=n.buildSetupExecutor({projectId:i.id,projectDir:i.dir}),m=n.buildDwsRunner();try{await No(l,async()=>{await Lo(m,async()=>{u({type:"task.started",task:c}),u({type:"setup.started",projectId:e}),await p("\u52A0\u8F7D\u914D\u7F6E\u4E0E\u5DE5\u4F5C\u6D41\u6A21\u677F","preflight"),await Mo({viewsPath:xo.resolve(i.dir,"views.yml"),configPath:F(i),autoUpdate:!0}).catch(O=>($o.warn("setup","setup \u524D\u540C\u6B65 views.yml \u5931\u8D25\uFF0C\u7EE7\u7EED\u4F7F\u7528 config \u4E2D\u7684\u89C6\u56FE\u914D\u7F6E",{error:T(O)}),null));let d=await Eo(F(i),{lenientEnv:!0}),k=await Fo(d.workflowTemplatePath,{envSource:d.envSource}),w=d.tracker.base_id==="__placeholder__"?void 0:d.tracker,j=w??k.tracker;if(j&&!j.table_id.trim()){let O=w?await _o(F(i)):d.workflowTemplatePath,M=w?d:{...d,tracker:j},me=await Co({trackerFilePath:O,settings:M,workflow:k,onProgress:s((ae,ce)=>p(ae,ce),"onProgress")});d={...d,tracker:me.tracker}}await p("\u5F00\u59CB\u521B\u5EFA\u5B57\u6BB5\u548C\u89C6\u56FE","sync_schema");let q=await Wo({settings:d,workflow:k,cleanEmptyRows:!0});for(let O of q.warnings)await p(`\u6CE8\u610F\uFF1A${O.message}`,"sync_schema");let ie=Yt(d,k),V=q.warnings.map(O=>O.message);await I(i);let z=await r.updateTask(i,c.id,{status:"succeeded",currentAction:"AI \u8868\u683C\u642D\u5EFA\u5B8C\u6210",checkpoint:"done",outputs:{tableUrl:We(d.tracker.base_id,d.tracker.table_id),baseId:d.tracker.base_id,tableId:d.tracker.table_id,schema:ie,warnings:V}});u({type:"task.succeeded",task:z}),u({type:"setup.done",tableUrl:We(d.tracker.base_id,d.tracker.table_id),baseId:d.tracker.base_id,tableId:d.tracker.table_id,schema:ie,warnings:V}),o.end()})})}catch(d){if(Ho(d)){let w=T(d),j=await r.updateTask(i,c.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${w}`,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"}});u({type:"task.blocked",task:j}),o.end();return}let k=await r.updateTask(i,c.id,{status:"failed",currentAction:"AI \u8868\u683C\u642D\u5EFA\u5931\u8D25",error:{code:E(d),message:T(d),retryable:!0,details:W(d)}});u({type:"task.failed",task:k}),u({type:"error",error:te(d)}),o.end()}}s(Be,"handleSetup");function Ho(e){let t=T(e).toLowerCase(),r=e instanceof Bo?e.stderr.toLowerCase():"",o=`${t} ${r}`;return["auth","login","permission","unauthorized","forbidden","access denied"].some(n=>o.includes(n))}s(Ho,"isSetupAuthBlocker");function Qt(e){e.post("/:projectId/tasks/:stage/:taskAction",C(async t=>{let{services:r,incoming:o,outgoing:n}=t.env,i=t.env.project,a=t.req.param("stage")??"",c=t.req.param("taskAction")??"",u=c==="start"||c==="continue"||c==="retry";if(u&&a==="setup"){await v(i,r),await Be(i.id,r.workspace,r.projectStore.instances,n,G(t));return}if(u&&a==="run"){await v(i,r),await Me(i.id,r.workspace,r.projectStore.instances,r.runtime,o,n);return}H(n,404,"NOT_FOUND","\u63A5\u53E3\u4E0D\u5B58\u5728")})),e.post("/:projectId/design/discovery",C(async t=>{let{services:r,outgoing:o}=t.env,n=t.env.project;await v(n,r),await Gt(n.id,r.workspace,r.projectStore.instances,o)})),e.post("/:projectId/design/discovery/confirm",C(async t=>{let{services:r,incoming:o,outgoing:n}=t.env,i=t.env.project;await v(i,r);let a=await b(o);await Kt(i.id,r.workspace,r.projectStore.instances,a,n)})),e.post("/:projectId/design",C(async t=>{let{services:r,incoming:o,outgoing:n}=t.env,i=t.env.project;await v(i,r);let a=new URL(t.req.url),c=Xe(a,o);await Vt(i.id,r.workspace,r.projectStore.instances,n,G(t),c),await pe(r,i,"design")})),e.post("/:projectId/setup",C(async t=>{let{services:r,outgoing:o}=t.env,n=t.env.project;await v(n,r),await Be(n.id,r.workspace,r.projectStore.instances,o,G(t)),await pe(r,n,"setup")})),e.post("/:projectId/run/records/:recordId/human-action",C(async t=>{let{services:r,incoming:o,outgoing:n}=t.env,i=t.env.project;await v(i,r),await It(i.id,r.workspace,r.projectStore.instances,r.runtime,t.req.param("recordId")??"",o,n)})),e.post("/:projectId/run",C(async t=>{let{services:r,incoming:o,outgoing:n}=t.env,i=t.env.project;await v(i,r),await Me(i.id,r.workspace,r.projectStore.instances,r.runtime,o,n)}))}s(Qt,"registerStageRoutes");function Xt(){let e=new Uo;return e.get("/",async t=>y(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 b(n),a=await r.projectStore.projects.create(o,{title:i.title,files:i.files});await v(a,r);let c={project:a};return a.files&&(c.files=a.files),a.warnings&&a.warnings.length>0&&(c.warnings=a.warnings),y(t,c)}),e.use("/:projectId",Ie),e.use("/:projectId/*",Ie),e.delete("/:projectId",async t=>{let{services:r,scope:o}=t.env,n=t.env.project;return r.runtime.runSessions.has(n.id)?_(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 ge(n),await et(r,n.id),y(t,{ok:!0,id:n.id}))}),e.patch("/:projectId",async t=>{let{services:r,scope:o,incoming:n}=t.env,i=await b(n);return y(t,{project:await r.projectStore.projects.updateMeta(t.env.project,{title:i.title},o)})}),e.post("/:projectId/init",C(async t=>{let{services:r,incoming:o,outgoing:n}=t.env,i=t.env.project;await v(i,r);let a=G(t);await bt(i.id,r.workspace,r.projectStore.instances,o,n,(c,u)=>a.bindProjectMachine(c,u)),await pe(r,i,"init")})),e.get("/:projectId/engine",async t=>{let r=t.env.project,o=G(t),n=await o.getProjectMachine(r.id);if(!n)return y(t,{machineId:null,machine:null,status:"unbound"});let i=o.isMachineOnline(n),c=(o.listMachines()??[]).find(u=>u.machineId===n);return y(t,{machineId:n,machine:c??null,status:i?"online":"offline"})}),e.put("/:projectId/engine",async t=>{let{incoming:r}=t.env,o=await b(r);return o.machineId?(await G(t).bindProjectMachine(t.env.project.id,o.machineId),y(t,{ok:!0,machineId:o.machineId})):_(t,422,"INVALID_INPUT","machineId \u4E0D\u80FD\u4E3A\u7A7A")}),e.get("/:projectId/files",async t=>{let r=t.env.project;return y(t,{project:r,files:await S(r)})}),e.put("/:projectId/files",async t=>{let{services:r,scope:o,incoming:n}=t.env,i=t.env.project,a=await b(n);if(!Array.isArray(a.files))return _(t,422,"INVALID_INPUT","files \u4E0D\u80FD\u4E3A\u7A7A");let c=await r.projectStore.projects.replaceFiles(i,a.files,o),u=await v(i,r);return y(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 b(n),c=await r.projectStore.projects.updateFiles(i,{upsert:a.upsert,remove:a.remove},o),u=await v(i,r);return y(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=qt(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 y(t,{...a,workflows:u})}return y(t,a)}),e.post("/:projectId/validate",async t=>{let r=t.env.project,o=le.join(r.dir,"workflow.yml"),n=le.join(r.dir,"recipes"),i=le.join(r.dir,"views.yml"),a=await qo(o,n,i);return await Rt(r,a),y(t,a)}),e.get("/:projectId/workflow",async t=>{let r=t.env.project,o=await zo(r.dir),n=le.join(r.dir,"workflow.yml"),i=await Zt(n,{envSource:o}).catch(()=>Zt(n)),a=le.join(r.dir,"recipes"),c=i.steps.map(u=>({...u,requires_agent:u.recipe?_t(a,u.recipe):!!u.prompt}));return y(t,{workflow:{...i,steps:c}})}),e.post("/:projectId/reset-workflow",async t=>{let r=await ct(t.env.project);return y(t,{project:r,files:await S(r)})}),e.get("/:projectId/state",async t=>{let{services:r}=t.env,o=t.env.project,n=await r.projectStore.instances.getRecoverableState(o);return y(t,ht(n,r.runtime.runSessions.get(o.id)))}),e.patch("/:projectId/table-id",C(async t=>{let{services:r,incoming:o,outgoing:n}=t.env;await At(t.env.project.id,r.workspace,r.projectStore.instances,o,n)})),zt(e),Qt(e),e}s(Xt,"projectRoutes");function er(){let e=new Jo;return e.onError((t,r)=>_(r,re(t),E(t),T(t),W(t))),e.notFound(t=>_(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(!(Ve(o,n)||n==="GET"&&o==="/api/public-config")&&!await t.env.services.identityService.authenticate(t.env.incoming))return Ye(t);await r()}),e.get("/api/health",t=>y(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 y(t,{authEnabled:t.env.services.identityService.authRequired,...r?{relayGatewayUrl:r}:{}})}),e.route("/api/projects",Xt()),e.get("/api/agents",t=>y(t,Go())),e}s(er,"createHonoApp");import{createApp as vn,logger as jn}from"aitable-workflow-core";function tr(e){return{create:s(async(t,r)=>{let o=await at(e),n=[];if(r?.files&&r.files.length>0){let i=await ye(o,r.files);n.push(...i.warnings);let a=await I(o);return r.title&&r.title.trim()&&(a=await ne(a,{title:r.title.trim()})),{...a,files:i.written,warnings:n}}if(r?.title&&r.title.trim()){let i=await ne(o,{title:r.title.trim()});return n.length?{...i,warnings:n}:{...i}}return n.length?{...o,warnings:n}:{...o}},"create"),list:s(async t=>_e(e),"list"),resolve:s(async(t,r)=>A(e,t),"resolve"),updateMeta:s(async(t,r,o)=>ne(t,r),"updateMeta"),delete:s(async(t,r)=>{await ge(t)},"delete"),updateFiles:s(async(t,r,o)=>(r.remove&&r.remove.length>0&&await $t(t,r.remove),r.upsert&&r.upsert.length>0&&await ye(t,r.upsert),await I(t),S(t)),"updateFiles"),replaceFiles:s(async(t,r,o)=>(await Ht(t,r),await I(t),S(t)),"replaceFiles"),exportProject:s(async(t,r)=>Ut(t),"exportProject"),syncHotFields:s(async()=>{},"syncHotFields")}}s(tr,"createLocalProjectRepository");import{randomUUID as rn}from"crypto";import{mkdir as or,readFile as nr,rm as on,writeFile as sr}from"fs/promises";import Y from"path";import{applyRunExecutionEvent as nn}from"aitable-workflow-core";var $e=["init","design","setup","run"];function Te(e,t){let r={...e.lastTasks,[t.stage]:t},o=rr(t.status)?t:e.activeTask?.id===t.id?null:e.activeTask;return B({id:t.projectId,dir:"",createdAt:"",updatedAt:t.updatedAt},{...e,projectId:t.projectId,stage:t.stage,activeTask:o,lastTasks:r,updatedAt:t.updatedAt})}s(Te,"mergeTaskIntoState");function B(e,t){let r=new Date().toISOString(),o=t.lastTasks??{},n=t.activeTask??Object.values(o).find(a=>a&&rr(a.status))??null,i=t.stage??n?.stage??en(o)?.stage??"init";return{projectId:t.projectId??e.id,stage:i,activeTask:n,lastTasks:o,stageRuntime:{init:he("init",o.init,n,o,i),design:he("design",o.design,n,o,i),setup:he("setup",o.setup,n,o,i),run:he("run",o.run,n,o,i)},runSnapshot:t.runSnapshot,updatedAt:t.updatedAt??e.updatedAt??r}}s(B,"normalizeProjectState");function rr(e){return e==="running"||e==="blocked"}s(rr,"isActiveStatus");function ve(e){return e==="failed"||e==="succeeded"||e==="cancelled"}s(ve,"isTerminalStatus");function he(e,t,r,o,n){let i=r?.stage===e?r:t;return i?{status:i.status,taskId:i.id,checkpoint:i.checkpoint,message:i.block?.message??i.error?.message??i.currentAction??tn(i.status),retryable:i.error?.retryable??(i.status==="blocked"||i.status==="failed"),block:i.block,error:i.error,recoveryAction:Yo(i)}:Ko(e,o,n)?{status:"succeeded",message:Vo(e),retryable:!1}:{status:"idle",message:"\u5C1A\u672A\u5F00\u59CB"}}s(he,"runtimeForStage");function Ko(e,t,r){let o=$e.indexOf(e);return $e.indexOf(r)>o?!0:$e.slice(o+1).some(i=>!!t[i])}s(Ko,"isPreviousStage");function Vo(e){return e==="init"?"\u914D\u7F6E\u5DF2\u5B8C\u6210":e==="design"?"\u5DE5\u4F5C\u6D41\u8BBE\u8BA1\u5B8C\u6210":e==="setup"?"AI \u8868\u683C\u642D\u5EFA\u5B8C\u6210":"\u8FD0\u884C\u9636\u6BB5\u5DF2\u5B8C\u6210"}s(Vo,"inferredSuccessMessage");function Yo(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??Qo(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:Zo(e.stage),description:e.error.message,api:{method:"POST",path:Xo(e)}}}s(Yo,"recoveryActionForTask");function Qo(e){return e==="setup"?"\u7EE7\u7EED\u642D\u5EFA":e==="design"?"\u7EE7\u7EED\u8BBE\u8BA1":e==="run"?"\u7EE7\u7EED\u8FD0\u884C":"\u7EE7\u7EED"}s(Qo,"continueLabel");function Zo(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(Zo,"retryLabel");function Xo(e){return e.stage==="design"?`/api/projects/${e.projectId}/design`:`/api/projects/${e.projectId}/tasks/${e.stage}/retry`}s(Xo,"retryPath");function en(e){return Object.values(e).filter(t=>!!t).sort((t,r)=>r.updatedAt.localeCompare(t.updatedAt))[0]??null}s(en,"latestTask");function tn(e){return e==="running"?"\u4EFB\u52A1\u8FD0\u884C\u4E2D":e==="blocked"?"\u4EFB\u52A1\u7B49\u5F85\u5904\u7406":e==="failed"?"\u4EFB\u52A1\u5931\u8D25":e==="succeeded"?"\u4EFB\u52A1\u5B8C\u6210":e==="cancelled"?"\u4EFB\u52A1\u5DF2\u53D6\u6D88":"\u5C1A\u672A\u5F00\u59CB"}s(tn,"statusMessage");var je=new Map,sn=/^[a-zA-Z0-9_-]+$/;function ir(e){return{getState:Q,getRecoverableState:fn,createTask:un,updateTask:pn,clearActiveTask:mn,appendRunSnapshotEvent:dn,updateRunSnapshotRecord:ln,deleteProjectState:s(t=>an(e,t),"deleteProjectState")}}s(ir,"createLocalWorkflowInstanceStore");async function an(e,t){Ue(t);let r=cn(e,t);await on(Y.join(r,"runtime"),{recursive:!0,force:!0})}s(an,"deleteProjectState");function cn(e,t){return Ue(t),Y.join(e.projectsRoot,t)}s(cn,"projectDir");async function un(e,t){return se(e,async()=>{await De(e);let r=await Q(e),o=(r.lastTasks[t]?.attempt??0)+1,n=new Date().toISOString(),i={id:`task_${rn().slice(0,8)}`,projectId:e.id,stage:t,status:"running",attempt:o,startedAt:n,updatedAt:n,currentAction:"\u4EFB\u52A1\u5DF2\u542F\u52A8"};return await Pe(e,i),await K(e,Te(r,i)),i})}s(un,"createTask");async function pn(e,t,r){return se(e,async()=>{let o=await wn(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??(ve(r.status)?n:o.completedAt),block:Object.prototype.hasOwnProperty.call(r,"block")?r.block:ve(r.status)?void 0:o.block};await Pe(e,i);let a=await Q(e);return await K(e,Te(a,i)),i})}s(pn,"updateTask");async function Q(e){await De(e);let t=await nr(cr(e),"utf8").catch(()=>"");if(t.trim()){let r=yn(t),o=B(e,r);return r.repaired&&await K(e,o),o}return B(e,{projectId:e.id,stage:"init",activeTask:null,lastTasks:{},updatedAt:e.updatedAt})}s(Q,"getState");async function dn(e,t){return se(e,async()=>{let r=await Q(e),o=nn(r.runSnapshot??{records:[],logs:[]},t),n=B(e,{...r,runSnapshot:o,updatedAt:new Date().toISOString()});return await K(e,n),n})}s(dn,"appendRunSnapshotEvent");async function ln(e,t,r,o){return se(e,async()=>{let n=await Q(e);if(!n.runSnapshot)return n;let i=Date.now(),a={records:hn(n.runSnapshot.records,t,u=>({...u,...r,updatedAt:i})),logs:o?Tn(n.runSnapshot.logs,t,r.currentStepId??"run","info",o):n.runSnapshot.logs},c=B(e,{...n,runSnapshot:a,updatedAt:new Date().toISOString()});return await K(e,c),c})}s(ln,"updateRunSnapshotRecord");async function fn(e){return se(e,async()=>{let t=await Q(e),r=t.lastTasks.design,o=await ut(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 Pe(e,u);let p=B(e,{...t,stage:t.stage==="design"?"setup":t.stage,activeTask:t.activeTask?.stage==="design"?null:t.activeTask,lastTasks:{...t.lastTasks,design:u},updatedAt:c});return await K(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=B(e,{...t,stage:"design",activeTask:null,lastTasks:{...t.lastTasks,design:i},updatedAt:n});return await Pe(e,i),await K(e,a),a})}s(fn,"getRecoverableState");async function mn(e,t){return se(e,async()=>{let r=await Q(e);if(r.activeTask?.id!==t)return r;let o=B(e,{...r,activeTask:null,updatedAt:new Date().toISOString()});return await K(e,o),o})}s(mn,"clearActiveTask");function He(e){return Y.join(e.dir,"runtime")}s(He,"runtimeDir");function gn(e){return Y.join(He(e),"artifacts")}s(gn,"artifactsDir");function ar(e){return Y.join(He(e),"tasks")}s(ar,"tasksDir");function cr(e){return Y.join(He(e),"state.json")}s(cr,"statePath");function ur(e,t){return Ue(t),Y.join(ar(e),`${t}.json`)}s(ur,"taskPath");async function De(e){await or(ar(e),{recursive:!0}),await or(gn(e),{recursive:!0})}s(De,"ensureRuntimeDirs");async function wn(e,t){let r=await nr(ur(e,t),"utf8");return JSON.parse(r)}s(wn,"readTask");async function Pe(e,t){await De(e),await sr(ur(e,t.id),`${JSON.stringify(t,null,2)}
21
+ `,"utf8")}s(Pe,"writeTask");async function K(e,t){await De(e),await sr(cr(e),`${JSON.stringify(t,null,2)}
22
+ `,"utf8")}s(K,"writeProjectState");async function se(e,t){let r=e.dir||e.id,n=(je.get(r)??Promise.resolve()).catch(()=>{}).then(t),i=n.then(()=>{},()=>{});je.set(r,i);try{return await n}finally{je.get(r)===i&&je.delete(r)}}s(se,"withProjectStateWrite");function yn(e){try{return JSON.parse(e)}catch(t){let r=kn(e);if(!r)throw t;return{...JSON.parse(r),repaired:!0}}}s(yn,"parseProjectStateJson");function kn(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(kn,"extractFirstJsonObject");function hn(e,t,r){return e.map(o=>o.id===t?r(o):o)}s(hn,"updateRunRecord");function Tn(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(Tn,"appendRunLog");function Ue(e){if(!sn.test(e))throw Object.assign(new Error(`\u975E\u6CD5\u9879\u76EE ID: ${e}`),{code:"INVALID_PROJECT_ID"})}s(Ue,"assertProjectId");var Se=class{static{s(this,"LocalProjectStore")}projects;instances;workflows;constructor(t){this.projects=tr(t),this.instances=ir(t)}async prepareWorkdir(t,r){return{project:await I(t),materialized:0,warnings:[]}}async commitWorkdir(t,r){}async cascadeDelete(t){}};var Oe=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 be=class{static{s(this,"LocalIdentityService")}authRequired=!1;authenticate(t){return Ge(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 pr(e={}){let t=e.workspace??it({dataRoot:e.dataRoot});return{workspace:t,runtime:yt(e.appFactory??((r,o)=>vn(r,{onRunEvent:o}))),projectStore:e.projectStore??new Se(t),execution:e.execution??new Oe,identityService:e.identityService??new be,extendApp:e.extendApp,webRoot:e.webRoot}}s(pr,"createServerServices");function dr(e=process.env){let t=e.AITABLE_RELAY_BOOTSTRAP_KEY;return typeof t=="string"&&t.trim()!==""?(jn.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(dr,"warnDeprecatedBootstrapKey");var Pn="127.0.0.1",ha=8787;function Ae(e){if(typeof e!="string")return;let t=e.trim();return t||void 0}s(Ae,"readEnvString");function Dn(e){let t=Ae(e);if(t===void 0)return;let r=Number.parseInt(t,10);return Number.isFinite(r)&&r>0?r:void 0}s(Dn,"readEnvPort");function lr(e={},t=process.env){let r=e.host??Ae(t.AITABLE_WORKFLOW_HOST)??Pn,o=e.port??Dn(t.AITABLE_WORKFLOW_PORT)??8787,n=e.dataRoot??Ae(t.AITABLE_WORKFLOW_DATA_DIR)??void 0,i=e.webRoot??Ae(t.AITABLE_WORKFLOW_WEB_ROOT)??void 0;return{host:r,port:o,dataRoot:n,webRoot:i}}s(lr,"resolveServeConfig");import fe from"path";import{readFile as Sn,access as On}from"fs/promises";var bn={".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 fr(e,t,r,o){let n=fe.resolve(e),i=fe.join(n,"index.html"),a=decodeURIComponent(t).replace(/^\/+/,""),c=a===""?i:fe.resolve(n,a),p=c===n||c.startsWith(n+fe.sep)?c:i;try{await On(p)}catch{p=i}try{let l=await Sn(p),m=fe.extname(p).toLowerCase(),d=bn[m]??"application/octet-stream";if(o.writeHead(200,{"Content-Type":d,"Content-Length":l.byteLength}),r.method==="HEAD"){o.end();return}o.end(l)}catch{H(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(fr,"serveStaticWeb");async function _a(e={}){dr();let t=lr({port:e.port,host:e.host,dataRoot:e.dataRoot,webRoot:e.webRoot}),r=e.services??pr({dataRoot:t.dataRoot,appFactory:e.appFactory,webRoot:t.webRoot}),o=An.createServer((n,i)=>{Rn(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(_a,"startServer");var mr=new WeakMap;function In(e){let t=mr.get(e);return t||(t=er(),e.extendApp?.(t),mr.set(e,t)),t}s(In,"appFor");async function Rn(e,t,r){if(Je(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 fr(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:xn(t.headers)}),a=await In(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(Rn,"handleRequest");function xn(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(xn,"toFetchHeaders");import gr from"path";function Ma(e){let t=gr.normalize(e).split(gr.sep).join("/");return t==="workflow.yml"?"workflow":t==="views.yml"?"views":t.startsWith("recipes/")?"recipe":"other"}s(Ma,"workflowFileKind");function Ba(){let e=new Map;return{withProjectLock(t,r){let n=(e.get(t)??Promise.resolve()).then(r,r);return e.set(t,n),n.then(()=>{e.get(t)===n&&e.delete(t)},()=>{e.get(t)===n&&e.delete(t)}).catch(()=>{}),n}}}s(Ba,"createProjectMutexStore");export{Pn as DEFAULT_SERVE_HOST,ha as DEFAULT_SERVE_PORT,Oe as LocalExecutionBackend,be as LocalIdentityService,Se as LocalProjectStore,te as apiError,Nt as assertSafeProjectPath,Mt as assertSafeRelativePath,As as assertSafeWorkflowPath,io as assertWritableKind,G as backendOf,We as buildAITableUrl,et as cascadeDeleteProject,de as checksumContent,pe as commitStageOnTerminal,F as configPath,er as createHonoApp,tr as createLocalProjectRepository,ir as createLocalWorkflowInstanceStore,at as createProject,Ba as createProjectMutexStore,yt as createServerRuntime,pr as createServerServices,it as createWorkspace,ge as deleteProject,_ as errorBody,E as errorCode,W as errorDetails,T as errorMessage,re as errorStatus,Rn as handleRequest,kt as handleRunRuntime,Lt as isForbiddenWritable,Sr as isStageActiveFor,ve as isTerminalStatus,y as jsonBody,Bt as kindOf,S as listProjectFiles,_e as listProjects,Te as mergeTaskIntoState,B as normalizeProjectState,ht as overlayRunSessionState,v as prepareWorkdirGuarded,C as raw,xr as readDesignManifest,b as readJson,$t as removeProjectFiles,Ht as replaceProjectFiles,ct as resetProjectWorkflow,A as resolveProject,Ie as resolveProjectMw,lr as resolveServeConfig,H as sendError,N as sendJson,_n as sendUnauthorized,_a as startServer,I as touchProject,Ye as unauthorized,ne as updateProjectMeta,ao as validateConfigScene,ut as validateDesignArtifacts,dr as warnDeprecatedBootstrapKey,Ma as workflowFileKind,Fe as writeDesignManifest,ye as writeProjectFiles};
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "aitable-workflow-server",
3
+ "version": "0.1.8",
4
+ "description": "aitable-workflow 服务端(server):纯 local 的 HTTP design/setup/run API(Hono)。relay gateway/PG/OSS 装配在 apps/gateway。",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "engines": {
18
+ "node": ">=20.18.0"
19
+ },
20
+ "publishConfig": {
21
+ "registry": "https://registry.npmjs.org",
22
+ "access": "public"
23
+ },
24
+ "dependencies": {
25
+ "aitable-workflow-core": "0.1.8",
26
+ "@hono/node-server": "^2.0.12",
27
+ "hono": "^4.12.32",
28
+ "yaml": "^2.8.1"
29
+ }
30
+ }