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